I've got this kind of test: ```class ClassBeingMoc...
# mockk
k
I've got this kind of test:
Copy code
class ClassBeingMocked(val arg: String) {
    fun foo(n: Int = arg.length) {
        println("foo($n) called")
    }
}

class ClassBeingTested(val a: ClassBeingMocked) {
    fun bar() {
        a.foo()
    }
}

class MyJunitTest {
    val a = mockk<ClassBeingMocked>(relaxed = true)
    val b = ClassBeingTested(a)

    @Test
    fun test1() {
        b.bar()
    }
}
Notice that
ClassBeingMocked.foo
takes a default parameter that depends on a property given as constructor parameter. The mock instance doesn't use this constructor parameter, as it's a mock.
ClassBeingTested
relies on the default value of this function. The above test throws
NullPointerException: Cannot invoke "String.length()" because "<parameter1>.arg" is null
. The stack trace starts at the line where
foo
is implemented, in class
ClassBeingMocked
. If
a
is a mock instance, why does the stack trace show that it's going to the real class? Furthermore, can I somehow fix this without changing
ClassBeingTested
to pass an actual parameter instead of the default?
m
my first thought is: how about providing a default value for
arg
in the constructor as well? otherwise, can you please try explicitly mocking the
foo
function, i.e. making the mock for
ClassBeingMocked
not relaxed?
k
Thanks for your quick response, Mattia. Unfortunately, in the real application, the arg in the constructor of
ClassBeingMocked
is not a String (I just used String for simplicity) but a complex dependency that's difficult to choose a default for. Also, trying
val a = mockk<ClassBeingMocked> { every foo(any()) just runs }
gives the same problem.
t
if you can rewrite as
Copy code
class ClassBeingMocked(val arg: String) {
  val foo: Int
    get() = arg.length
  fun foo(n: Int = foo) {
    println("foo($n) called")
  }
}

class MyJunitTest {
    val a = mockk<ClassBeingMocked>(relaxed = true)
    val b = ClassBeingTested(a)

    @Test
    fun test1() {
        every { a.foo } returns 2
        b.bar()
    }
}
that works. It would be interesting to see a more real example, it smells like a design issue also just forfeiting the default parameter might be an option, then you don't even have to change your test
Copy code
class ClassBeingMocked(val arg: String) {
  fun foo() = foo(arg.length)

  fun foo(n: Int) {
    println("foo($n) called")
  }
}
1
k
Thanks, I did end up doing something similar to your last suggestion in the end (adding a function without the default parameter). In the real application, the dependency (arg) is injected by Spring, but I prefer not to use Spring in tests.