The Kotlin mocking library mockk lets's you mock a class like this:
class Foo {
fun foo() = "foo"
}
val foo = mockk<Foo>()
every { foo.foo() } returns "bar" // π make the foo mock return "bar"
foo.foo() shouldBe "bar"It also throws an error if you try to mock an instance that is no mock:
val foo = Foo()
every { foo.foo() } returns "bar" // π trying to mock an instance that is no mockMissing mocked calls inside every { ... } block: make sure the object inside the block is a mock
But if you look at the following example, mocking non-mock instances seems to work:
class Bar {
fun bar() = "bar"
}
class Foo(private val bar: Bar) {
fun foo() = bar.bar()
}
val foo = Foo(mockk())
every { foo.foo() } returns "baz" // π make the foo instance return "baz"
foo.foo() shouldBe "baz"The above code works! foo is no mock but the foo.foo() call actually returns baz.
During the every call, mockk switches to the recording mode.
It records calls to mocks and assigns the return value to the recorded invocation.
Calls to non-mocks are not recorded, but the invocation takes place nonetheless.
In the example foo.foo() is called. foo.foo() internally calls bar.bar() whereat bar is a mock.
That is, the bar.bar() call is recorded and the return value baz is assigned to the bar.bar() call.
In short, syntactically it looks like we are mocking the non-mock foo,
but in reality we are accidentally interacting with the mock bar which foo delegates to.
This becomes more obvious when we have Foo.foo() modify the output of bar.bar():
class Bar {
fun bar() = "bar"
}
class Foo(private val bar: Bar) {
fun foo() = "foo-" + bar.bar() // π prepend `foo-` to its return value
}
val foo = Foo(mockk())
every { foo.foo() } returns "baz"
foo.foo() shouldBe "foo-baz" // π only the returned value of `bar.bar()` is modifiedMocking non-mocks using mockk is not possible.
If you find yourself in a situation where you think you are mocking a non-mock, you are probably interacting with a mock that the non-mock delegates to.