Home > Mobile >  mockk calls original class only instead of mockked results
mockk calls original class only instead of mockked results

Time:07-18

I have this class that I need to test. I am thinking it is because I am creating a new instance of the class A:

class A {

      fun getName() = "Name"

}


   class B {

    private a = A()

    fun amend(){
      return "${a.getName()}-BName"
    }

}

Now I want to test class B in BTest.kt

  @Test
  fun amendTest(){
  
    val b = B()

    val cls = mockk<A>()

    //everytime getName is called return empty string
    every { cls.getName() } returns ""

    //call ammend method
    val newName = b.amend()

    //make sure the getName is called now
    verify  { cls.getName()}

    Truth.assertThat(newName).isEqualTo("-BName")

   }

Basically I want to alter the output of A.getName method to return empty string during tests.

CodePudding user response:

When you create val cls = mockk<A>(), you have made a single instance (cls) that is a mock of A, but that doesn't mean that every instance of A is mocked. The one inside of B is still a non-mocked instance. As written, B has a hard dependence on A which makes it difficult/impossible to test them in isolation. This is a perfect example of one of the benefits of dependency injection. If you defined B like this:

class B(private val a: A) {
    fun amend(): String {
      return "${a.getName()}-BName"
    }
}

where you inject the implementation of A, then in your test you could do this:

val cls = mockk<A>()
every { cls.getName() } returns ""
val b = B(cls)

then b would be using your mocked instance of A internally. If you have a lot of these dependencies to inject and it gets cumbersome to create them to pass in every time you create B in your production code, you could always make a companion builder for B like this

class B(private val a: A, private val c: C, private val d: D) {

    fun amend(): String {
        return "${a.getName()}-BName"
    }
    
    companion object {
        fun build(): B {
            return B(A(), C(), D())
        }
    }
}

so in your production code you can call val b = B.build() but in your unit tests you can call val b = B(aMock, cMock, dMock)

CodePudding user response:

Although it would be advisable that the instance of class A be provided from outside (by constructor or by seter), it is possible do to test with Mockk.

By means of dynamic calls:

val b = spyk(B(), recordPrivateCalls = true)
val cls = mockk<A>()
every { cls. getName() } returns "whatever"
every { b getProperty "a" } returns cls

val result = b.amend()

verify { cls.getName() }  

assertEquals("whatever-BName", result)

B must be spy type to capture private properties and to be verified

  • Related