How to unit-test Slick 3.x dbio combinators? - scala

I have a class with a single method that I want to unit-test:
#Singleton
class RegistrationWorkflow #Inject()(userService: UserService,
addUserValidator: RegisterUserValidator,
db: Database) {
def registerUser(registerForm: RegisterUserForm): Future[Vector[FormError]] = {
val dbActions = addUserValidator.validate(registerForm).flatMap({ validation =>
if (validation.isEmpty) {
userService.add(User(GUID.shortGuid(),
registerForm.username,
registerForm.email,
BCrypt.hashpw(registerForm.password, BCrypt.gensalt())))
.andThen(DBIO.successful(validation))
} else {
DBIO.successful(validation)
}
}).transactionally
db.run(dbActions)
}
}
addUserValidator validates the form and returns a Vector of form errors. If there were no errors, the user is inserted into database. I am returning the form errors because in the controller I'm either returning a 201 or a 400 with a list of errors.
I have written a specs2 test for this:
class RegistrationWorkflowTest extends Specification with Mockito with TestUtils {
"RegistrationControllerWorkflow.registerUser" should {
"insert a user to database if validation succeeds" in new Fixture {
registerUserValidatorMock.validate(testUserFormData) returns DBIO.successful(Vector())
userServiceMock.add(any) returns DBIO.successful(1)
val result = await(target.registerUser(testUserFormData))
result.isEmpty must beTrue
there was one(registerUserValidatorMock).validate(testUserFormData)
there was one(userServiceMock).add(beLike[User] { case User(_, testUser.username, testUser.email, _) => ok })
}
"return error collection if validation failed" in new Fixture {
registerUserValidatorMock.validate(testUserFormData) returns DBIO.successful(Vector(FormError("field", Vector("error"))))
val result = await(target.registerUser(testUserFormData))
result.size must beEqualTo(1)
result.contains(FormError("field", Vector("error"))) must beTrue
there was one(registerUserValidatorMock).validate(testUserFormData)
there was no(userServiceMock).add(any)
}
}
trait Fixture extends Scope with MockDatabase {
val userServiceMock = mock[UserService]
val registerUserValidatorMock = mock[RegisterUserValidator]
val target = new RegistrationWorkflow(userServiceMock, registerUserValidatorMock, db)
val testUser = UserFactory.baseUser()
val testUserFormData = RegisterUserFactory.baseRegisterUserForm()
}
}
The issue with this test is that it just asserts that userService.add was called. This means that I can change my implementation to the following:
val dbActions = addUserValidator.validate(registerForm).flatMap({ validation =>
if (validation.isEmpty) {
userService.add(User(GUID.shortGuid(),
registerForm.username,
registerForm.email,
BCrypt.hashpw(registerForm.password, BCrypt.gensalt())))
DBIO.successful(validation)
} else {
DBIO.successful(validation)
}
}).transactionally
db.run(dbActions)
The test still passes, but the user will not be inserted, because I am not ussing andThen combinator on the DBIO that was returned by userService.add method.
I know that I could use an in memory database and then assert that the user was actually inserted, but I don't want to do that because I already tested userService.add method separately with an in memory database and now I want to test registerUser method without calling any dependencies.

Related

Async before and after for creating and dropping scala slick tables in scalatest

I'm trying to figure out a way to have async before and after statements where the next test cases aren't run until the completion of the action inside of the test case. In my case, it is the creating and dropping a table inside of a database
val table = TableQuery[BlockHeaderTable]
val dbConfig: DatabaseConfig[PostgresDriver] = DatabaseConfig.forConfig("databaseUrl")
val database: Database = dbConfig.db
before {
//Awaits need to be used to make sure this is fully executed before the next test case starts
//TODO: Figure out a way to make this asynchronous
Await.result(database.run(table.schema.create), 10.seconds)
}
"BlockHeaderDAO" must "store a blockheader in the database, then read it from the database" in {
//...
}
it must "delete a block header in the database" in {
//...
}
after {
//Awaits need to be used to make sure this is fully executed before the next test case starts
//TODO: Figure out a way to make this asynchronous
Await.result(database.run(table.schema.drop),10.seconds)
}
Is there a simple way I can remove these Await calls inside of my before and after functions?
Unfortunately, #Jeffrey Chung's solution hanged for me (since futureValue actually awaits internally). This is what I ended up doing:
import org.scalatest.{AsyncFreeSpec, FutureOutcome}
import scala.concurrent.Future
class TestTest extends AsyncFreeSpec /* Could be any AsyncSpec. */ {
// Do whatever setup you need here.
def setup(): Future[_] = ???
// Cleanup whatever you need here.
def tearDown(): Future[_] = ???
override def withFixture(test: NoArgAsyncTest) = new FutureOutcome(for {
_ <- setup()
result <- super.withFixture(test).toFuture
_ <- tearDown()
} yield result)
}
The following is the testing approach that Dennis Vriend takes in his slick-3.2.0-test project.
First, define a dropCreateSchema method. This method attempts to create a table; if that attempt fails (because, for example, the table already exists), it drops, then creates, the table:
def dropCreateSchema: Future[Unit] = {
val schema = BlockHeaderTable.schema
db.run(schema.create)
.recoverWith {
case t: Throwable =>
db.run(DBIO.seq(schema.drop, schema.create))
}
}
Second, define a createEntries method that populates the table with some sample data for use in each test case:
def createEntries: Future[Unit] = {
val setup = DBIO.seq(
// insert some rows
BlockHeaderTable ++= Seq(
BlockHeaderTableRow(/* ... */),
// ...
)
).transactionally
db.run(setup)
}
Third, define an initialize method that calls the above two methods sequentially:
def initialize: Future[Unit] = for {
_ <- dropCreateSchema
_ <- createEntries
} yield ()
In the test class, mix in the ScalaFutures trait. For example:
class TestSpec extends FlatSpec
with Matchers
with ScalaFutures
with BeforeAndAfterAll
with BeforeAndAfterEach {
// ...
}
Also in the test class, define an implicit conversion from a Future to a Try, and override the beforeEach method to call initialize:
implicit val timeout: Timeout = 10.seconds
implicit class PimpedFuture[T](self: Future[T]) {
def toTry: Try[T] = Try(self.futureValue)
}
override protected def beforeEach(): Unit = {
blockHeaderRepo.initialize // in this example, initialize is defined in a repo class
.toTry recover {
case t: Throwable =>
log.error("Could not initialize the database", t)
} should be a 'success
}
override protected def afterAll(): Unit = {
db.close()
}
With the above pieces in place, there is no need for Await.
You can simplify #Jeffrey Chung
A simplified dropCreateSchema method:
def dropCreateSchema: Future[Unit] = {
val schema = users.schema
db.run(DBIO.seq(schema.dropIfExists, schema.create))
}
Also in the test class, I simplified beforeEach method that calls initialize. I removed an implicit conversion from a Future to a Try, and use onComplete callback:
override protected def beforeEach(): Unit = {
initialize.onComplete(f =>
f recover {
case t: Throwable =>
log.error("Could not initialize the database", t)
} should be a 'success)
}
override protected def afterAll(): Unit = {
db.close()
}

How to write unit test for Play Framework JSON readers

I wish to write unit test for the shipmentNumberValidator method, where Reads is play.api.libs.json.Reads. How do I do it?
object Validator {
def shipmentNumberValidator(): Reads[String] =
Reads.filter(ValidationError(ErrorConstants.BAD_SHIPMENT_NUMBER))(_.matches(BarcodePatterns.ShipmentNumber))
}
The unit test should pass it a shipment number, and the method returns either successfully the shipment number, or the error message if the shipment number format doesn't match. I don't know how to pass values to this method.
The method is usually used in companion objects for implicit reads, e.g.:
object ShipmentOrder {
implicit val shipmentOrderReads: Reads[ShipmentOrder] = (
(JsPath \ "id").read[String](Validator.missingFieldValidator("id") keepAnd Validator.shipmentNumberValidator())(ShipmentOrder.apply _)
}
Depending on the test framework you use (e.g. specs2), you can test expectations as following:
Doing a Json.parse("{expectedJson}") must be JsResult(expectedInstance)
Doing a Json.parse("{unexpectedJson}") must be JsError(_)
Using specs2:
import play.api.libs.json._
"JSON" should {
"be successfully parsed when expected" in {
Json.parse("""{"expected":"json"}""") must beLike[JsResult[ShipmentOrder]] {
case JsSuccess(parsed, _) =>
parsed must_== expectedShipmentOrder
}
}
"fail to be parsed if expected" in {
Json.parse("""{"unexpected":"json"}""") must beLike[JsResult[ShipmentOrder]] {
case JsError(details) =>
ok // possible check `details`
}
}
}

How to really test the backend and avoid the use of MockWS?

I am fairly new to Playframework (and Scala in general). At the moment I am writing specs2 unit test that test python backend service, using MockWS. Yes, I found it great to test whether my frontend works using the same output as what the backend generated. But what if I really want to test the backend? How can I avoid the use of MockWS?
"SomeService" should {
object Props {
object Urls {
val BaseUrl = "http://localhost:9002/someservice/"
val Test = s"${BaseUrl}test"
}
val test = Test(value=true)
}
class NormalContext extends Scope {
val ws = MockWS {
case (GET, Props.Urls.Test) => Action {
Ok(Json.toJson(Props.test))
}
}
val abtestService = new SomeService(ws, Props.Urls.BaseUrl, 100)
}
"test mock" should {
"return True" when {
"call without any argument" in new NormalContext {
val result = someService.getTest()
whenReady(result) { result =>
result must equal (true)
}
}
}
}
}
I try to introduce this new context, but got the exception: java.lang.RuntimeException: There is no started application
Any suggestion?
class LiveBackEndContext extends Scope {
import play.api.Play.current
val ws: WSClient = WS.client
val abtestService = new AbTestService(ws, Props.Urls.BaseUrl, 100)
}
"test without mock" should {
"return True" when {
"call without any argument" in new LiveBackEndContext {
val result = abtestService.getTest()
whenReady(result) { result =>
result must equal (true)
}
}
}
}

Integration test with ScalaTest and Future

I'm trying to build my application around asynchronous method so I use Future with Scala. For more information, it's elastic4s lib in order to query an Elasticsearch server.
I begin with this pattern, so I first created this method:
def insert(person: Person) = {
client.execute {
index into "index" / "type" source person
} onComplete {
case Success(s) => {
logger.info("{} has been inserted successfully", interaction.toString)
something_to_do()
}
case Failure(t) => logger.warn("An error has occured: {}", t.getMessage)
}
}
If I understand well, this method is not testable.
When I read this post, I have to create a method which returns a Future, and I have to use ScalaFutures (for ScalaTest) to test my async method.
So I execute this pattern, and I've created two method like this:
def insert(person: Person): Future[IndexResponse] = {
client.execute {
index into "index" / "type" source person
}
}
def insertToEs(person: Person): Unit = {
insert(person) onComplete {
case Success(s) => {
logger.info("{} has been inserted successfully", person.toString)
something_to_do()
}
case Failure(t) => logger.warn("An error has occured: {}", t.getMessage)
}
}
So now it's easy to test my first method, but how do I test the insertToEs method?
More generally, I would like to create integration tests: Kafka (embedded) to Elasticsearch (embedded).
I have this code which calls previous insertToEs method:
def receive: Unit = {
...
val iterator = stream.iterator()
while(iterator.hasNext) {
insertToEs(iterator.next.message)
}
}
I've created a test on receive method, but it looks like the integration test ends before the methods have been executed.
#RunWith(classOf[JUnitRunner])
class ToEsSpec extends FlatSpec with Matchers with BeforeAndAfterAll with ScalaFutures {
override def beforeAll = {
run_kafka_server
run_elasticsearch_server
}
override def afterAll = {
shutdown
}
it should "receive message from kafka and insert to es" {
send_to_kafka(json)
receive
assert if inserted in es
}
}
Could you please advice me ? Thanks
see http://doc.scalatest.org/2.2.4/index.html#org.scalatest.concurrent.Futures
In some of my testcase, I used : the following 2 patterns :
with Future
val actualFuture = ...
actualFuture.futureValue mustEqual ... //block until actualFuture isCompleted
with async, but no Future, I repeate test during a Time until success or timeout with http://doc.scalatest.org/2.2.4/index.html#org.scalatest.concurrent.Eventually

Access ScalaTest test name from inside test?

Is it possible to access the name of the currently executing test, from within a ScalaTest test? (And how would I do it?)
Background:
I'm testing that my Data Access Object eventually throws an OverQuotaException if a user e.g. creates too many pages. These tests take rather long to run. To feel happier, I'd like to print the progress to stdout — and since there are quite many tests, I'd like to include the test name in the output, so I know what test is currently being run.
(I didn't find any seemingly relevant function here: http://www.artima.com/docs-scalatest-2.0.M5/#org.scalatest.FreeSpec )
Example:
"QuotaCharger can" - {
"charge and decline quota consumers" - {
"charge a per site IP number (guest user)" in {
// ... Here, a guest user post very many comments until it's over quota.
// This takes a little while, and there are many similar tests.
// ---> Here <--- I'd like to access the string:
// "charge a per site IP number (guest user)",
// is that possible somehow?
}
The intended way to do that is to override withFixture and capture the test data. In this use case, it is better to override withFixture in fixture.FreeSpec so you can pass the test data into each test rather than using a var. Info on that is here:
http://www.artima.com/docs-scalatest-2.0.M5/org/scalatest/FreeSpec.html#withFixtureNoArgTest
When I saw your question this morning I realized ScalaTest should have a trait that does this, so I just added one. It will be in 2.0.M6, the next milestone release, but in the meantime you can use a local copy. Here it is:
import org.scalatest._
/**
* Trait that when mixed into a <code>fixture.Suite</code> passes the
* <code>TestData</code> passed to <code>withFixture</code> as a fixture into each test.
*
* #author Bill Venners
*/
trait TestDataFixture { this: fixture.Suite =>
/**
* The type of the fixture, which is <code>TestData</code>.
*/
type FixtureParam = TestData
/**
* Invoke the test function, passing to the the test function to itself, because
* in addition to being the test function, it is the <code>TestData</code> for the test.
*
* <p>
* To enable stacking of traits that define <code>withFixture(NoArgTest)</code>, this method does not
* invoke the test function directly. Instead, it delegates responsibility for invoking the test function
* to <code>withFixture(NoArgTest)</code>.
* </p>
*
* #param test the <code>OneArgTest</code> to invoke, passing in the
* <code>TestData</code> fixture
*/
def withFixture(test: OneArgTest) {
withFixture(test.toNoArgTest(test))
}
}
You would use it like this:
import org.scalatest._
class MySpec extends fixture.FreeSpec with TestDataFixture {
"this technique" - {
"should work" in { td =>
assert(td.name == "this technique should work")
}
"should be easy" in { td =>
assert(td.name == "this technique should be easy")
}
}
}
Create your own trait, let say RichFreeSpec.
trait RichFreeSpec extends Free {
protected final class RichFreeSpecStringWrapper(name: scala.Predef.String) {
def in(f: String => scala.Unit) {
def f2 = f(name)
new WordSpecStringWrapper(string).in(f2)
}
}
protected implicit def convertToRichFreeSpecStringWrapper(n: scala.Predef.String): = {
new RichFreeSpecStringWrapper(n)
}
}
Than just use:
"sth" in { testName =>
...
}
Of course, you can go further and implement the full name hierarchy.
You can use BeforeAndAfterEachTestData for what you need.
If you need access to the test case name in the beforeEach or afterEach method.
class MyTestSuite with AnyFunSuiteLike with BeforeAndAfterEachTestData {
override def beforeEach(testData: TestData): Unit = {
testData.name // do whatever.
super.beforeEach(testData)
}
}
If you need access to the test case name in the test case itself, then you can use a thread local approach
private val currentTestCaseName = new ThreadLocal[String]
override def beforeEach(testData: TestData): Unit = {
currentTestCaseName.set(testData.name)
super.beforeEach(testData)
}
test("fancy test") {
currentTestCaseName.get() // do whatever
}
Here's a solution. Extend this class instead of FreeSpec. License: CC0.
Edit: This doesn't work with concurrent tests though.
(The difference between this approach and the other answer, is that 1) here there's a currentTestName field, and in the other answer the test name is passed to the test body, and 2) this test name includes all test branch names concatenated + the actual test name, whereas the other answer's test name is exactly the test name (without test branch names).)
(Ooops, you'd need to use getOrElse ... instead of my lovely getOrDie.)
/**
* Adds a field `currentTestName` that you can use inside a FreeSpec test,
* if you for example have many tests that take rather long, and you wonder
* which one is currently running.
*/
trait RichFreeSpec extends FreeSpec {
private var _currentTestName: Option[String] = None
def currentTestName = _currentTestName getOrDie "DwE90RXP2"
protected override def runTest(testName: String, args: org.scalatest.Args) {
_currentTestName = Some(testName)
super.runTest(testName, args)
}
}
If the intent is to be able to access the test name from anywhere, as was suggested by #kajmanus in previous comments, a ThreadLocal fits the bill nicely.
You could define a case class to store the info you require for the current test context. e.g.,
case class TestContext(name: Option[String] = None)
object TestContext {
val currentTest: ThreadLocal[TestContext] =
ThreadLocal.withInitial(() => TestContext())
}
Then define a trait your various specs will extend. e.g.,
trait BaseFunSpec
extends AnyFunSpec
...
{
override protected def withFixture(test: NoArgTest): Outcome = {
try {
TestContext.currentTest.set(TestContext(name = Some(test.name)))
super.withFixture(test)
} finally {
TestContext.currentTest.remove()
}
}
}
Finally, you can access the current test context you've set for the current thread (which in this example is purely the test name) from anywhere within the current thread as needed. e.g.,
def cachedResults(bytes: Array[Byte], fileType: String): Unit = {
TestContext.currentTest.get().name match {
case Some(testname) =>
import scala.util.Using
val file = new File("target", s"${testname}.${fileType}")
Using(new BufferedOutputStream(new FileOutputStream(file))) { os =>
os.write(bytes)
}
case None => throw new IllegalStateException("Unknown test context")
}
}
This will work whether you're running tests in parallel or not, assuming you're not processing things asynchronously (i.e., in another thread).
A cleaner usage of this is to create purposed actors. e.g.,
case class TestContext(name: Option[String] = None)
object TestContext {
val currentTest: ThreadLocal[TestContext] = ThreadLocal.withInitial(() => TestContext())
class TestNamer {
def currentName: String = currentTest.get().name match {
case Some(testname) => testname
case None => throw new IllegalStateException("No test context available")
}
}
class TestContextWriter(testNamer: TestNamer = new TestNamer()) {
def cachedBytes(bytes: Array[Byte], extension: String): Array[Byte] = {
import java.io.{BufferedOutputStream, File, FileOutputStream}
import scala.util.Using
val file = new File("target", s"${testNamer.currentName}.${extension}")
Using(new BufferedOutputStream(new FileOutputStream(file))) { outstream =>
outstream.write(bytes)
}
bytes
}
}
}
And inject as needed:
trait BaseFunSpec {
val testContextWriter = new TestContextWriter()
def fetchRawResults(...): Array[Byte] = {
...
testContextWriter.cachedBytes(bytes, "pdf")
}
}