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
Related
in my test class I want to do something before all the tests begin, so I did something like this:
class ApplicationSpec extends FreeSpec with OneServerPerSuite with BeforeAndAfterAll {
override protected def beforeAll(): Unit = {
doSomething()
}
"Application Test" - {
"first test" in {
...
}
}
}
but I get an error:
Exception encountered when invoking run on a nested suite - There is
no started application java.lang.RuntimeException: There is no started application
it only works if i try to use doSomething() inside the tests...
how can i fix this?
thanks!
I am assuming doSomething() performs some operation that is dependent on application state.
Try this:
class ApplicationSpec extends FreeSpec with BeforeAndAfterAll with OneServerPerSuite{
override protected def beforeAll(): Unit = {
doSomething()
}
"Application Test" - {
"first test" in {
...
}
}
}
The problem is you are possibly mixin linearization in wrong order.
By mixin OneSerPerSuite before BeforeAndAfterAll, the order in which the super.run() is called is reversed, leading to beforeAll() being called before Application start.
From the git repo of the two projects:
//BeforeAndAfterAll
abstract override def run(testName: Option[String], args: Args): Status = {
if (!args.runTestInNewInstance && (expectedTestCount(args.filter) > 0 || invokeBeforeAllAndAfterAllEvenIfNoTestsAreExpected))
beforeAll()
val (runStatus, thrownException) =
try {
(super.run(testName, args), None)
}
catch {
case e: Exception => (FailedStatus, Some(e))
}
...
}
//OneServerPerSuite
abstract override def run(testName: Option[String], args: Args): Status = {
val testServer = TestServer(port, app)
testServer.start()
try {
val newConfigMap = args.configMap + ("org.scalatestplus.play.app" -> app) + ("org.scalatestplus.play.port" -> port)
val newArgs = args.copy(configMap = newConfigMap)
val status = super.run(testName, newArgs)
status.whenCompleted { _ => testServer.stop() }
status
} catch { // In case the suite aborts, ensure the server is stopped
case ex: Throwable =>
testServer.stop()
throw ex
}
}
So by putting the OneServerPerSuite trait in the end, it will first initialize the application, then call super.run() which will call the run method inside BeforeAndAfterAll which will execute beforeAll() and then invoke super.run() of FreeSpec which will execute the tests.
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()
}
I'm using Play Framework 2.5 and trying to test one of my DAO class based on the example here.
But my class under test has dependency injection and I wonder how to mock it.
I tried to do something but it seems a bit clumsy, and I would like help to improve it.
Here is my code :
class MyController #Inject()(val mydao: MyDAOClass) extends Controller {
def getData = Action { request =>
mydao.fetchData
}
}
class MyDAOClass #Inject()(ws: WSClient) {
def urls(): List[String] = { List("url1", "url2") }
// fetch data on different servers and should merge it.
def fetchData(): Future[List[String]] = {
val futures: List[Future[WSResponse]] = urls map { url =>
ws.url(url + "/some/data").withRequestTimeout(10000.millis).get()
}
futures map { future =>
future onComplete {
case Success(resp) => println(resp.json)
case Failure(t) => println("An error has occured: " + t.printStackTrace)
}
}
Future(List())
}
}
Here is my test code :
trait DAOTrait {
def urls: List[String]
}
class MyDAOClass extends PlaySpec with MockitoSugar {
"MyDAOClass" should {
"get a list of data" in {
Server.withRouter() {
case GET(p"/url1/some/data") => Action {
Results.Ok(Json.arr(Json.obj("name" -> "data1")))
}
} { implicit port =>
val mockMyDAOClass = mock[DAOTrait]
when(mockMyDAOClass.urls) thenReturn List("url1")
implicit val materializer = Play.current.materializer
WsTestClient.withClient { client =>
val mydao = new MyDAOClass(client) {
override def urls = mockMyDAOClass.urls
}
val result = Await.result(
mydao.fetchData(), 10.seconds)
result mustEqual List("data1")
}
}
}
}
}
The code is not finished in MyDAOClass, but it is enough to start implementing tests.
1) How to mock class that have dependency injection ?
2) When testing this code I have the following exception in MyDAOClass case Failure(t)
java.net.ConnectException: http://localhost:40183
at org.asynchttpclient.netty.channel.NettyConnectListener.onFailure(NettyConnectListener.java:137)
at org.asynchttpclient.netty.request.NettyChannelConnector$1.onFailure(NettyChannelConnector.java:106)
at org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:28)
at org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:20)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:683)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:564)
at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:425)
at io.netty.channel.nio.AbstractNioChannel.doClose(AbstractNioChannel.java:462)
at io.netty.channel.socket.nio.NioSocketChannel.doClose(NioSocketChannel.java:236)
at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:611)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:590)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:534)
at io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:361)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.nio.channels.ClosedChannelException
but it should return Result.Ok instead.
3) Any advice welcome.
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.
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.ScalaFutures
import org.apache.thrift.TApplicationException
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution {
it should "throw org.apache.thrift.TApplicationException for invalid Ids" in {
val future: Future[Response] = ThriftClient.thriftRequest
whenReady(future) {
res => {
intercept[TApplicationException] {
}
}
}
}
}
Question: How do you assert expected failures in Futures without blocking? The above doesn't work, the exception is thrown before the intercept block.
I know this is probably a bit late, but ScalaTest provides this feature out of the box (I believe since version 2) by mixing in the ScalaFutures trait, or using it directly in your test functions. Behold!
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f.failed) { e =>
e shouldBe a [SomeExceptionType]
}
}
Or you can perform some other assertions in there. Basically, if your future doesn't fail like you expect, the test will fail. If it fails, but throws a different exception, the test will fail. Nice and easy! =]
cheeky edit:
You can also use this method to test anything that returns a future:
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f) { s =>
// run assertions against the object returned in the future
}
}
Most recent edit!
I just wanted to update this answer with more useful information based on newer versions of Scala test. The various spec traits now all have async support, so instead of extending, say, WordSpec, you would instead extend AsyncWordSpec, and instead of relying on the whenReady calls as above, you would just map over your futures directly in the test.
Example:
class SomeSpec extends Async[*]Spec with Matchers {
...
test("some test") {
someObject.funcThatReturnsAFutureOfSomething map { something =>
// run assertions against the 'something' returned in the future
}
}
}
This was buried in a comment as well, but Scalatest's FutureValues mixin has you covered.
Just use f.failed.futureValue shouldBe an[TApplicationException]
Note: leaving this answer because the OP found it helpful, but for Scala Futures see the other answer.
This is a bit boilerplated, but Waiter from AsyncAssertions:
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.{ ScalaFutures, AsyncAssertions, PatienceConfiguration }
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
import util._
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[UnsupportedOperationException] {
w.await
}
}
}
given
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
class Goof {
def goof(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new UnsupportedOperationException
}
def goofy(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new NullPointerException
}
def foog(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
7
}
}
In other words,
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
import Helper._
f.failing[UnsupportedOperationException]
}
}
object Helper {
implicit class Failing[A](val f: Future[A]) extends Assertions with AsyncAssertions {
def failing[T <: Throwable](implicit m: Manifest[T]) = {
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[T] {
w.await
}
}
}
}
Or, if you have multiple futures and you want the first non-conforming future to fail the test:
trait FailHelper extends Assertions with AsyncAssertions with PatienceConfiguration {
def failingWith[T <: Throwable : Manifest](fs: Future[_]*)(implicit p: PatienceConfig) {
val count = new java.util.concurrent.atomic.AtomicInteger(fs.size)
val w = new Waiter
for (f <- fs) f onComplete {
case Success(i) =>
w(intercept[T](i))
println(s"Bad success $i")
w.dismiss()
case Failure(e: T) =>
println(s"Failed $e OK, count ${count.get}")
w(intercept[T](throw e))
if (count.decrementAndGet == 0) w.dismiss()
case Failure(e) =>
println(s"Failed $e Bad")
w(intercept[T](throw e))
w.dismiss()
}
w.await()(p)
}
}
with usage
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with FailHelper {
it should "throw for invalid Ids" in {
val sut = new Goof()
import sut._
val patienceConfig = null // shadow the implicit
implicit val p = PatienceConfig(timeout = 10 seconds)
// all should fail this way
//failingWith[UnsupportedOperationException](goof(), goofy(3), foog(5))
//failingWith[UnsupportedOperationException](goof(), foog(5))
failingWith[UnsupportedOperationException](goof(), goof(2), goof(3))
}
}
Inspired by this unloved answer.
ScalaTest 3.0 adds async versions of the spec traits like AsyncFreeSpec:
import org.scalatest.{AsyncFlatSpec, Matchers}
import scala.concurrent.Future
class ScratchSpec extends AsyncFlatSpec with Matchers {
def thriftRequest = Future { throw new Exception() }
it should "throw exception" in {
recoverToSucceededIf[Exception] {
thriftRequest
}
}
}
Addition to Brian Low's answer, I found a nice explanation for recoverToSucceededIf. This is available in all Async styles (from ScalaTest 3):
Failed futures can be tested in two ways: using recoverToSucceededIf or recoverToExceptionIf
recoverToSucceededIf is used for asserting the type of the exception the future ends in:
"return UserNotFoundException" when {
"the user does not exist" in {
recoverToSucceededIf[UserNotFoundException](userService.findUser("1"))
}
}
recoverToExceptionIf is useful when you want to test some of the exception's fields:
"return UserAlreadyExistsException" when {
"adding a user with existing username" in {
recoverToExceptionIf[UserAlreadyExistsException] {
userService.addUser(user)
}.map { ex =>
ex.message shouldBe s"User with username: $username already exists!"
}
}
}
See the whole blog from Tudor Zgureanu
—
What's new in ScalaTest 3
You can also try this Something Simple and Short
test("some test throwing SQL Exception") {
val f: Future[Something] = someObject.giveMeAFuture
recoverToSucceededIf[SQLException](f)
}