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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With