dataProcs
dataProcs

Reputation: 55

How to test a method by mocking nested methods?

I'm trying to test an Object.method which contains some nested methods from a Trait apart of some calculations. These nested methods have to be mocked (they access to a DB so I want to mock their responses).

When I call the real Object.method, it should skip the nested methods call and retrieve what I want. I've tried mocking them but test is still calling them.

Here's my example source code:

trait MyTrait {
def myMethodToMock(a: String): String
}

object MyObject extends MyTrait {
def myParentMethod(a:String) = {
val b = myMethodToMock(a)
val c = a + b
c
}
}

Then in my test:

val myTraitMock = mock[MyTrait]
when(myTraitMock.myMethodToMock(a)).thenReturn(b)

//Then I call the parent method:
assert(MyObject.myParentMethod(a) equals c)

It throws a NullPointerException as it's still accessing to myMethodToMock

Upvotes: 0

Views: 287

Answers (2)

Dima
Dima

Reputation: 40508

Your code does not compile, so I am going to guess some things of what you are actually trying to do here ...

You are stubbing a method on a mock, and then calling it on a completely unrelated instance. No wonder it does not work.

A good rule of thumb (and the best practice) is to never mock classes you are actually testing. Split everything you want to mock and test separately into a separate class. This is also known as single responsibility principle (each component should be responsible for a single thing).

 trait MyTrait {
    def myMethodToMock(a: String): String
 }
 object MyTrait extends MyTrait {
    def myMethodtoMock(a: String) = ???
 }

 class MyObject(helper: MyTrait = MyTrait) {
    def myParentMethod(a: String) = a + helper.myMethodToMock(a) 
 }
 object MyObject extends MyObject()

Now, you can write your test like this:

val myTraitMock = mock[MyTrait]
when(myTraitMock.myMethodToMock(any)).thenReturn("b")
new MyObject(myTraitMock).myParentMethod("a") shouldBe "ab"
verify(myTraitMock).myMethodToMock("a")

The main difference here is that you are passing your mock into the object's constructor, so that when it calls the method, it will be the one you stubbed, not the implementation provided by the default class.

Upvotes: 3

ultrasecr.eth
ultrasecr.eth

Reputation: 1497

You should use composition rather than inheritance, so you can inject an instance of MyTrait that can be a mock or the real one

Upvotes: 0

Related Questions