Say I have the following object I want to test
object ObjectToTest {
def apply() = {
val example = List.fill(5)(CaseClassExample("here", "there"))
ChildObject(example.tail) + " " + ChildObject2(example.tail)
}
}
where both child objects look like this
object ChildObject2 {
def apply(param1: Seq[CaseClassExample]) = {
"not mocked"
}
}
object ChildObject {
def apply(param1: Seq[CaseClassExample]) = {
"not mocked"
}
}
Is it possible to write a test that mocks the two child objects being used in the object under test?
Possible? Yes. Advised? No Idea.
We could write the following tests uisng mockito-scala and scalatest to mock both objects
class ObjectToTestSpec extends AnyWordSpec with MockitoSugar with ArgumentMatchersSugar {
"ObjectToTest" should {
"return not mocked when not mocked" in {
ObjectToTest.apply2() mustEqual "not mocked not mocked"
}
"return mocked when mocked anySeq" in {
withObjectMocked[ChildObject.type] {
withObjectMocked[ChildObject2.type ] {
when(ChildObject(anySeq[CaseClassExample])) thenReturn "mocked"
when(ChildObject2(anySeq[CaseClassExample])) thenReturn "mocked"
ObjectToTest.apply2() mustEqual "mocked mocked"
}
}
}
"return mocked when mocked eqTo" in {
withObjectMocked[ChildObject.type] {
withObjectMocked[ChildObject2.type] {
when(ChildObject(eqTo(List.fill(4)(CaseClassExample("here","there"))))) thenReturn "mocked"
when(ChildObject2(eqTo(List.fill(4)(CaseClassExample("here","there"))))) thenReturn "mocked"
ObjectToTest.apply2() mustEqual "mocked mocked"
}
}
}
}
}
Related
I am trying to write unit test for a function under object1.
object Object1 {
def main(sysArgs: Array[String]): Unit = {
val inputDF: DataFrame = UtilObject.getInput()
}
}
object UtilObject {
def getInput(){
...
}
}
To write Unit test, I am using MockitoSugar.
"object1Main" should "should make correct calls" in {
val inputArgs = Array("abc")
val util = mock[UtilObject.type]
when(util.getInput().thenReturn(inputData))
Object1.main(inputArgs)
}
While running the test, it doesn't consider the util mock and just execute the getInput() function.
I think I am missing some sort of injection here. Any ideas?
Thanks in advance!
Mocking Scala objects should be impossible conceptually speaking. An object in Scala is a pure singleton. That means there can only be one member of that type at any time.
mockito-scala can mock Scala objects via reflection. I'll use a result type of String, instead of a DataFrame, but the idea is the same:
object UtilObject {
def getInput(): String = {
// ...
"done"
}
}
object Object1 {
def main(sysArgs: Array[String]): String = {
val inputDF: String = UtilObject.getInput()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val inputArgs = Array("abc")
withObjectMocked[UtilObject.type] {
UtilObject.getInput() returns "mocked!"
Object1.main(inputArgs) shouldBe "mocked!"
}
Object1.main(inputArgs) shouldBe "done"
}
}
This mocks the singleton's method only inside the block of withObjectMocked.
Usually such powerful techniques often tend to be overused or misused, so I don't generally recommend them, unless the design cannot be refactored.
Luckily, yours can: the easiest way is to use Dependency Injection with a class or a function. For DI with a class you require to convert the object being mocked into a class:
class UtilObject {
def getInput(): String = {
// ...
"done"
}
}
object Object1 {
def main(sysArgs: Array[String], ut: UtilObject): String = {
val inputDF: String = ut.getInput()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val inputArgs = Array("abc")
val util = mock[UtilObject]
when(util.getInput()).thenReturn("mocked!")
Object1.main(inputArgs, util) shouldBe "mocked!"
}
}
For DI with a function you need to lift the method you want to mock into a function:
object UtilObject {
def getInput(): String = {
// ...
"done"
}
}
object Object1 {
def main(sysArgs: Array[String], f: () => String = UtilObject.getInput): String = {
val inputDF: String = f()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val inputArgs = Array("abc")
val f = mock[() => String]
when(f()).thenReturn("mocked!")
Object1.main(inputArgs, f) shouldBe "mocked!"
}
}
Since the function takes no arguments, you can convert it into a by-name parameter. I'll leave that to you.
Lastly, another way is to create a trait with the method you want to mock and extend that with the object. But now Object1 requires being a class and have a reference to the object being mocked:
object UtilObject extends Utils {
def getInput(): String = {
// ...
"done"
}
}
trait Utils {
def getInput(): String
}
class Object1 {
val uo: Utils = UtilObject
def main(sysArgs: Array[String]): String = {
val inputDF: String = uo.getInput()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val classUnderTest = new Object1 {
override val uo = mock[Utils]
}
val inputArgs = Array("abc")
when(classUnderTest.uo.getInput()).thenReturn("mocked!")
classUnderTest.main(inputArgs) shouldBe "mocked!"
}
}
As you can see, there are a few ways to go. Neither is inherently wrong. It mostly depends on your requirements(e.g. you can't afford adding a dependency just for one UT), needs(e.g. does the object I am testing really needs to be an object or can I make it a class?), guidelines (e.g. your team decided to avoid using powerful testing frameworks that rely on reflection and use DI as much as possible instead) and personal preferences.
I have a simple companion Object that is creating a connection pool that should be globally static.
object CoreDataSource {
val pool : DataSource = getDataSource
def getDataSource: DataSource = {
...
}
}
I then use that in a variety of classes like...
class Query {
var source: DataSource = CoreDataSource.pool
def getSalutations: String = {
val conn : Connection = source.getConnection
...
}
}
Now I would like to test the getSalutations without the getDataSource actually getting called. Is there a way to mock out this call so that when the initial getDataSource call happens it hits a mock instead of the actual function?
Here is the skeleton of my test suite...
#RunWith(classOf[JUnitRunner])
class MySuite extends AnyFunSuite with MockitoSugar {
test("Test mocking the Datasource") {
// Mock object here
}
}
I Also tried...
var pool : Option[DataSource] = None
def getConnection: Connection = {
if(pool.isEmpty)
pool = getDataSource
pool.get.getConnection
}
def getDataSource: Option[DataSource] = {
...
}
}
#RunWith(classOf[JUnitRunner])
class MySuite extends AnyFunSuite with MockitoSugar with Matchers {
test("Test mocking the Datasource") {
withObjectMocked[CoreDataSource.type]{
val source = mock[DataSource]
when(CoreDataSource.getDataSource).thenReturn(Some(source))
...
assert(result == "")
}
}
}
But this throws
Mockito cannot mock/spy because :
- final class
I am not going to accept my workaround because it is ugly but this ended up working...
class CoreDataSource {
def getConnection = {
CoreDataSource.getConnection
}
}
object CoreDataSource {
var pool : Option[DataSource] = None
def getConnection: Connection = {
if(pool.isEmpty)
pool = getDataSource
pool.get.getConnection
}
def getDataSource: Option[DataSource] = {
...
}
}
class Query(ds: CoreDataSource = new CoreDataSource) {
def getSalutations: String = {
...
}
}
Then the following will work...
test("Single record returned") {
val cds = mock[CoreDataSource]
val source = mock[Connection]
doNothing.when(source).close()
when(cds.getConnection).thenReturn(source)
val q = new Query(cds)
val result = q.getSalutations
assert(result == "This is the test value")
}
I'd like to reuse mock declarations accross tests (if possible).
Here is a minimal non-working example using ScalaTest and Mockito. I'm expecting the yes value in the first test but I get the other value.
It seems that the latest Mockito.when is the one applied for all test clauses.
Is there a way to avoid declaring mocks in each in clause?
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.scalatest.{Matchers, WordSpec}
class ReuseMocksSpec extends WordSpec with Matchers with MockitoSugar {
"A test" when {
val service = mock[Service]
"sharing mocks among tests" should {
when(service.getVal).thenReturn("yes")
"get yes value" in {
service.getVal should be("yes")
}
}
"sharing mocks among other tests" should {
when(service.getVal).thenReturn("other")
"get other value" in {
service.getVal should be("other")
}
}
}
trait Service {
def getVal: String
}
}
I reviewed the way I designed it and am now using a function to build my mocks:
def withValue(value: String)(body: (Service => String)) = {
val service = mock[Service]
when(service.getVal).thenReturn(value)
body(service)
}
The test class would become:
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.scalatest.{Matchers, WordSpec}
class ReuseMocksSpec extends WordSpec with Matchers with MockitoSugar {
"A test" when {
"sharing mocks among tests" should {
"get yes value" in {
val value = withValue("yes") { service =>
service.getVal
}
value should be("yes")
}
}
"sharing mocks among other tests" should {
"get other value" in {
val value = withValue("other") { service =>
service.getVal
}
value should be("other")
}
}
}
def withValue(value: String)(body: (Service => String)) = {
val service = mock[Service]
when(service.getVal).thenReturn(value)
body(service)
}
trait Service {
def getVal: String
}
}
I don't know if it's the cleanest and easiest way to do it but it works...
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)
}
}
}
}
I have a spec like the following written:
class MySpec extends Specification {
"A" should {
"be true" in {
true must beEqualTo(true)
}
}
"B" should {
"be false" in {
false must beEqualTo(false)
}
}
...
}
How do I/Can I specify a before/after statement to execute only within the "B" block (for instance) for each test.
You can create a context for your test:
trait Context extends BeforeAfter {
def before: Any = println("Doing setup")
def after: Any = println("Done. Cleanup")
}
"B" should {
"be false" in new Context {
false must beEqualTo(false)
}
}
You can have a look at this link if you need to do some tricky things with specs2.