How to make JUnit4 lifecycle callbacks work in ScalaTest with JUnitRunner? - scala

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")
}
}

Related

Is it possible to mock / override dependencies / imports in Scala?

I have some code looking like this:
package org.samidarko.actors
import org.samidarko.helpers.Lib
class Monitoring extends Actor {
override def receive: Receive = {
case Tick =>
Lib.sendNotification()
}
}
Is there a way to mock/stub Lib from ScalaTest like with proxyquire for nodejs?
I read that I could use dependency injection but I would rather not do that
Is my only alternative is to pass my lib as class parameter?
class Monitoring(lib: Lib) extends Actor {
Any advice to make it more testable? Thanks
EDIT:
Xavier Guihot's answer is an interesting approach of the problem but I choose to change the code for testing purpose.
I'm passing the Lib as parameter and I'm mocking with mockito, it makes the code easier to test and to maintain than shadowing the scope.
This answer only uses scalatest and doesn't impact the source code:
Basic solution:
Let's say you have this src class (the one you want to test and for which you want to mock the dependency):
package com.my.code
import com.lib.LibHelper
class MyClass() {
def myFunction(): String = LibHelper.help()
}
and this library dependency (which you want to mock / override when testing MyClass):
package com.lib
object LibHelper {
def help(): String = "hello world"
}
The idea is to create a class in your test folder which will override/shadow the library. The class will have the same name and the same package as the one you want to mock. In src/test/scala/com/external/lib, you can create LibHelper.scala which contains this code:
package com.lib
object LibHelper {
def help(): String = "hello world - overriden"
}
And this way you can test your code the usual way:
package com.my.code
import org.scalatest.FunSuite
class MyClassTest extends FunSuite {
test("my_test") {
assert(new MyClass().myFunction() === "hello world - overriden")
}
}
Improved solution which allows setting the behavior of the mock for each test:
Previous code is clear and simple but the mocked behavior of LibHelper is the same for all tests. And one might want to have a method of LibHelper produce different outputs. We can thus consider setting a mutable variable in the LibHelper and updating the variable before each test in order to set the desired behavior of LibHelper. (This only works if LibHelper is an object)
The shadowing LibHelper (the one in src/test/scala/com/external/lib) should be replaced with:
package com.lib
object LibHelper {
var testName = "test_1"
def help(): String =
testName match {
case "test_1" => "hello world - overriden - test 1"
case "test_2" => "hello world - overriden - test 2"
}
}
And the scalatest class should become:
package com.my.code
import com.lib.LibHelper
import org.scalatest.FunSuite
class MyClassTest extends FunSuite {
test("test_1") {
LibHelper.testName = "test_1"
assert(new MyClass().myFunction() === "hello world - overriden - test 1")
}
test("test_2") {
LibHelper.testName = "test_2"
assert(new MyClass().myFunction() === "hello world - overriden - test 2")
}
}
Very important precision, since we're using a global variable, it is compulsory to force scalatest to run test in sequence (not in parallel). The associated scalatest option (to be included in build.sbt) is:
parallelExecution in Test := false
Not a complete answer (as I don't know AOP very well), but to put you in the right direction, this is possible through Java lib called AspectJ:
https://blog.jayway.com/2007/02/16/static-mock-using-aspectj/
https://www.cakesolutions.net/teamblogs/2013/08/07/aspectj-with-akka-scala
Example in pseudocode (without going into details):
class Mock extends MockAspect {
#Pointcut("execution (* org.samidarko.helpers.Lib.sendNotification(..))")
def intercept() {...}
}
The low level basics of this approach are Dynamic Proxies: https://dzone.com/articles/java-dynamic-proxy. However, you can mock static methods too (maybe you'll have to add word static into the pattern).

scalatest Flatspec: Timeout for entire class

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

Spark Unit Testing: How to initialize sc only once for all the Suites using FunSuite

I want to write spark unit test cases and I am using FunSuite for it.
But i want that my sparkContext is initialized only once , used by all the Suites and then is killed when all Suites completes.
abstract class baseClass extends FunSuite with BeforeAndAfter{
before {
println("initialize spark context")
}
after {
println("kill spark context")
}
}
#RunWith(classOf[JUnitRunner])
class A extends baseClass{
test("for class A"){
//assert
}
#RunWith(classOf[JUnitRunner])
class B extends baseClass{
test(for class b){
//assert
}
}
but when i run sbt test
I can see println statement baseClass has been called from both the tests. Obsiously When the object is created for both the classes A and B , Abstract
baseclass is called.
But then how can we achieve my purpose i.e spark context is iniliazed only once while all the test cases are run
Option 1: Use the excellent https://github.com/holdenk/spark-testing-base library that does exactly that (and provides many other nice treats). After following the readme, it's as simle as mixing-in SharedSparkContext instead of your baseClass, and you'll have an sc: SparkContext value ready to use in your test
Option 2: to do it yourself, you'd want to mix-in BeforeAndAfterAll and not BeforeAndAfter, and implement beforeAll and afterAll, which is exactly what the above-mentioned SharedSparkContext does.
I strongly recommend using the spark-testing-base library in order to manage the lifecycle of a sparkContext or sparkSession during your tests.
You won't have to pollute your tests by overriding the beforeAll, afterAll methods and managing the lifecycle of the sparkSession/sparkContext.
You can share one sparkSession/sparkContext for all the tests by overriding the following method :
def reuseContextIfPossible: Boolean = true
for more details : https://github.com/holdenk/spark-testing-base/wiki/SharedSparkContext
I hope it helps!
If you really want to share the context between suites - you'll have to make it static. Then you can use a lazy value to make it start on first use. As for shutting it down - you can leave it to the automatic Shutdown hook created each time a context is created.
It would look something like:
abstract class SparkSuiteBase extends FunSuite {
lazy val sparkContext = SparkSuiteBase.sparkContext
}
// putting the Spark Context inside an object allows reusing it between tests
object SparkSuiteBase {
private lazy val sparkContext = ??? // create the context here
}

Final clean up in specs2

I am writing a specs2 Unittest for my scala software. The execution is working well. The only problem I have, is that I need to clean up after all test are finished. I just cannot find any solution for that. Is there a way to execute some functions after all test are finished?
You need to add a Step at the end of your specification:
import org.specs2.mutable._
class MySpec extends Specification {
// lots of examples here
// cleanup there
step(cleanUp())
}
You can try to use After with After and implement def after function.
Example:
class Context extends Specification {
....
}
trait trees extends mutable.After {
def after = cleanupDB
}

#Test method in Scala trait not found

does somebody know why #Test annotated methods which are inherited from a Scala trait are not found by the JUnit 4 test runner? It gives me "No JUnit tests found".
class FooTests extends BarTesting
But the following modification works?
import junit.framework.TestCase
class FooTests extends TestCase with BarTesting
In the Scala trait BarTesting I defined methods with the following signature.
#Test
final def testBar() {
// ...
}
This is indeed a bug in Eclipse. You can raise as such if you like. http://www.assembla.com/spaces/scala-ide/tickets.
When you extend TestCase the test is being run because it starts with test, not because of the annotation. There was a problem with recognition of annotations, which is how the junit stuff works, and I haven't looked to see if it is fixed yet to make the junit stuff work.
Your best bet is to:
Raise the bug against scala-ide
Add #RunWith[classOf[JUnit]) to your class
The following works:
trait BarTesting {
#Test final def testBar() {
println("Hello world")
}
}
#RunWith(classOf[JUnit4])
class FooTesting extends BarTesting {
}
And I'll try and fix the bug.
EDIT: In the latest versions of scala-ide (as of 9 November 2011), this now works.