With the scalatest Flatspec and the TimeLimits trait I can set a timeout for a line of code like so:
import org.scalatest.time.SpanSugar._
import org.scalatest.concurrent.TimeLimits
import org.scalatest.FlatSpec
class MyTestClass extends Flatspec with TimeLimits {
"My thread" must "complete on time" in {
failAfter(100 millis) { infiniteLoop() }
// I have also tried cancelAfter
}
}
This should fail due to a timeout. However, when I run this test in Intellij it just runs forever.
Furthermore, I do not want to have to rewrite the timeout for every method, instead I would like to configure it once for the entire class. The PatienceConfig claims to do that, but it does not seem to do anything. The test is still runs forever.
import org.scalatest.FlatSpec
import org.scalatest.time.{Millis, Span}
import org.scalatest.concurrent.{Eventually, IntegrationPatience}
class MyTestClass extends Flatspec with Eventually with IntegrationPatience {
implicit val defaultPatience = PatienceConfig(timeout=Span(100, Millis))
"My thread" must "complete on time" in {
inifiniteLoop()
}
}
I looked for a solution myself. came a cross this answer, it worked for me.
I am using flatspec, added the trait TimeLimitedTests
with TimeLimitedTests
then inside the code I wrote my limit for each of the tests val timeLimit: Span = Span(2000, Millis)
which is defined by the trait (we are overriding it).
Finally it didn't work until I overriden the interrupter as suggested by Rumoku in the referenced answer by
override val defaultTestInterruptor: Interruptor = new Interruptor {
override def apply(testThread: Thread): Unit = {
println("Kindly die")
testThread.stop() // deprecated. unsafe. do not use
}}
I hope this helps
Related
I am writing unit tests for an akka actor model implementation. The system contains classes and traits that need to be initialised. My issue lies with the testing of the methods. When I mock required parameters for a class, it removes the intelij compiler error, however all of the variables are set to null.
I have attempted to use
when(mock.answer).thenReturn(42)
and directly assigning the variables
val mock.answer = 42
The above two through compilation errors. "When" is not recognised and directly assigning values cases a runtime error.
Any insight would be much appreciated.
I am not sure if I understood your issue correctly, but try the self contained code snippet below and let me know if it is not clear enough:
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{FunSuite, Matchers}
import org.mockito.Mockito.when
#RunWith(classOf[JUnitRunner])
class MyTest extends FunSuite with Matchers with MockitoSugar {
trait MyMock {
def answer: Int
}
test("my mock") {
val myMock = mock[MyMock]
when(myMock.answer).thenReturn(42)
myMock.answer should be(42)
}
}
Given the the following singleton Object in Scala:
package demo
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.io.StdIn
object WebServer extends App {
implicit val system = ActorSystem("myActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
val route = {
path("api" / "done-as-promised") {
get {
complete {
Future.successful("done")
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
And the following unit test
package demo
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Inspectors, Matchers, WordSpec}
class WebServerSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with Inspectors with ScalatestRouteTest {
"The WebServer /done-as-promised" should {
"return done" in {
// tests:
Get("/api/done-as-promised") ~> WebServer.route ~> check {
status.intValue() shouldEqual 200
responseAs[String] shouldEqual "done"
}
}
}
}
I get the following error:
[ERROR] [04/19/2016 07:12:18.995]
[ScalaTest-run-running-WebServerSpec]
[akka.actor.ActorSystemImpl(demo-WebServerSpec)] Error during
processing of request
HttpRequest(HttpMethod(GET),http://example.com/api/done-as-promised,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))
java.lang.NullPointerException at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:33)
at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:29)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:162)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:150)
It took me a while to figure out. The thing is: removing the extends app will make the test succeed.
The reason for the problem is that when WebServer is declared as extends App, it uses the DelayedInit functionality of the App trait. Because of this, the initialization code in the contructor is not added to the constructor of the WebServer object. Instead is called when the main method is called on the WebServer. So when he references the "route" inside the tests, those are all coming up null.
Mixing in the DelayedInit trait (App extends from DelayedInit) will rewrite your class or object template. Instead of adding your val's and var's to the constructor, it will be added to the def delayedInit(body: => Unit) hook (inaccessible to user code). Apparently this one is called whenever the main method is called.
You can verify this by simply calling "main" on the WebServer inside the test. If you do this, then the test will pass. This is because calling main triggers the initialization resulting in those objects being created.
Generally speaking though the right solution is probably to move the routing to somewhere else, rather than having it inside of the base App.
According to ScalaTest documentation it's possible to use JUnitRunner to run tests. My assumption was, if it runs with JUnitRunner, its callback methods (i.e. methods marked with #Before or #After annotation should work as well. But apparently my assumption was wrong. I've created simple example to demonstrate it:
import org.junit.Before
import org.junit.runner.RunWith
import org.scalatest.{FunSuite, _}
import org.scalatest.junit.JUnitRunner
#RunWith(classOf[JUnitRunner])
class Test extends FunSuite {
#Before
def before() = {
println("before test")
}
test("nothing") {
println("test is started")
}
}
If you run this example, you'll see test is started line, but not before test.
I'm aware of ScalaTest lifecycle callbacks, but the thing is I need to make JUnit callbacks work somehow. I want to write a test for Play 2.4 application and the thing it's play.test.WithBrowser class relies on JUnit callbacks. I found this workaround for the issue:
var testBrowser : TestBrowser = _
before {
new WithBrowser {
override def createBrowser(): Unit = {
super.createBrowser()
testBrowser = browser
}
}.createBrowser()
}
but I believe it's quite ugly and I suspect that there is a better way.
So, my question is if it's possible to make these JUnit lifecycle callbacks work with ScalaTest? And if it is possible, how to do that?
You should be able to do this using JUnitSuite instead of FunSuite. The documentation is here:
http://www.scalatest.org/getting_started_with_junit_4_in_scala
http://doc.scalatest.org/2.2.4/index.html#org.scalatest.junit.JUnitSuite
I did just like Noah suggested and it worked. Just in case anyone stumbles upon the same issue with Play 2.x and WithBrowser, I ended up with a test like this:
class IntegrationSpec extends WithBrowser with JUnitSuiteLike with Matchers {
#Test def workWithBrowser() {
browser.goTo("http://localhost:9000")
browser.pageSource should include("Add Person")
}
}
I am trying to write a test case for my application with akka-http. One of the testcases is given below:
import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.testkit.{ ScalatestRouteTest}
import com.reactore.common.core.{CommonCoreSystem, CommonActors, BootedCommonCoreSystem}
import scala.concurrent.duration._
import com.reactore.common.feature.referencedata.{DepartmentRepository, DepartmentService, DepartmentController, DepartmentRest}
import org.scalatest.concurrent.AsyncAssertions
import org.scalatest.time.Span
import org.scalatest.{WordSpec, Matchers}
import akka.http.scaladsl.model.StatusCodes._
/**
* Created by krishna on 18/6/15.
*/
class DepartmentITTest extends WordSpec with Matchers with ScalatestRouteTest with CommonCoreSystem with CommonActors {
// override val departmentRouter = system.actorOf(Props(classOf[DepartmentService], DepartmentRepository), Constants.DEPARTMENT_ROUTER_NAME)
val deptRoute = (new DepartmentRest(departmentRouter)(DepartmentController).deptRoutes)
implicit val timeout = AsyncAssertions.timeout(Span.convertDurationToSpan(5.second))
val departmentJson = """{"id":13,"name":"ENGP22","shortCode":"ENGP2","parentDepartmentId":null,"mineId":null,"photoName":null,"isRemoved":false,"isPendingForApproval":false,"createdBy":0,"createDate":"2015-03-09 00:00:00","modifiedBy":0,"modifiedDate":"2015-03-09 00:00:00"}"""
val header = RawHeader("apiKey", "xxxxx")
"Service" should {
"return department by id" in {
Get("/departments/13").withHeaders(header) ~> deptRoute ~> check {
// Thread.sleep(500)
status shouldBe OK
responseAs[String] shouldBe departmentJson
}
}
}
}
When I run this, it works correctly sometimes, and sometimes I get the error as Request was neither completed nor rejected within 1 second. I added a Thread.sleep for making it work now. I know that is not the correct solution. Could anyone tell me how to make the test wait for more than 1 second?
The following is working for me:
import akka.actor.ActorSystem
import scala.concurrent.duration._
import spray.testkit.ScalatestRouteTest
class RouteSpec extends ScalatestRouteTest {
implicit def default(implicit system: ActorSystem) = RouteTestTimeout(2.second)
...
You can use the "eventually" matcher in ScalaTest to wait for a condition to become true:
eventually { status shouldBe OK }
http://www.artima.com/docs-scalatest-2.0.M5/org/scalatest/concurrent/Eventually.html
That should suffice if the Thread.sleep you commented out above fixes things for you.
However, it looks to me like the actual error is that the timeout that the RouteTest trait is using is too short. The error message "Request was neither completed nor rejected within 1 second." comes from RouteTestResultComponent via akka.http.scaladsl.testkit.RouteTest.
I think that the Thread.sleep is a distraction. The default timeout for routing tests is 1 second; see akka.http.scaladsl.testkit.RouteTestTimeout.default. You provide a 5 second implicit timeout in your code, but I think it's of a different type. Try making a RouteTestTimeout implicitly available with a longer timeout.
you can simply update the config to dilate the timeout
akka {
test {
# factor by which to scale timeouts during tests, e.g. to account for shared
# build system load
timefactor = 3.0
}
}
I am new to entire ecosystem including Scala, Akka and ScalaTest
I am working on a problem where my Actor gives call to external system.
case object LogProcessRequest
class LProcessor extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
LogReaderDisruptor main(Array())
}
}
The LogReaderDisruptor main(Array()) is a Java class that does many other things.
The test I have currently looks like
class LProcessorSpec extends UnitTestSpec("testSystem") {
"A mocked log processor" should {
"be called" in {
val logProcessorActor = system.actorOf(Props[LProcessor])
logProcessorActor ! LogProcessRequest
}
}
}
where UnitTestSpec looks like (and inspired from here)
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.matchers.MustMatchers
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
abstract class UnitTestSpec(name: String)
extends TestKit(ActorSystem(name))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll() {
system.shutdown()
}
}
Question
How can I mock the call to LogReaderDisruptor main(Array()) and verify that it was called?
I am coming from Java, JUnit, Mockito land and something that I would have done here would be
doNothing().when(logReaderDisruptor).main(Matchers.<String>anyVararg())
verify(logReaderDisruptor, times(1)).main(Matchers.<String>anyVararg())
I am not sure how to translate that with ScalaTest here.
Also, This code may not be idiomatic, since I am very new and learning
There are a few ways to do this. The kind of OO way is to wrap logDisrupter as an object and pass it in. I would set up a companion object to instantiate the actor. Something like below. Then you can pass alternate implementation. You can also achieve a similar approach by using traits and mixing in an alternative logDisrupter only as needed.
object LProcessor {
def props(logDisrupter : LogDisrupter) = Props(new LProcessor(logDisrupter))
}
class LProcessor(logDisrupter : LogDisrupter) extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
logDisrupter.doSomething();
}
}
Then instatiate as
val logProcessorActor = system.actorOf(LProcessor.props(logDisrupter))