0

I am trying to mock a private function call in kotlin with mockk. Running into the following error:

Class is final. Put @MockKJUnit4Runner on your test or add MockK Java Agent instrumentation to make all classes 'open'

I fully understand the implications of trying to test a private method, however at this point its what I need to do.

Code:

class ClassToTest {
  private fun privateMethod(text: String): String {
    return "Hello $text"
  }
}

class TestClass {

   @Before
   fun setup() {
      MockitoAnnotations.openMocks(this)
   }

   @Test
   fun testSplitNumbers() {
      val clazz = spyk<ClassToTest>()

      val method = clazz.javaClass.getDeclaredMethod("privateMethod", String::class.java)
      method.isAccessible = true
      val result = method.invoke(clazz, "Test") as? String
      Assert.assertEquals("Hello Test", result)
   }
}
dimnnv
  • 678
  • 3
  • 8
  • 21
lostintranslation
  • 23,756
  • 50
  • 159
  • 262
  • No. Just make the method `internal` and mark it with an annotation like `@VisibleForTesting`. Also, you can't mock or spy on final classes - that's what the error actually says. Add an `open` modifier to the class and relevant methods. – Jorn May 11 '23 at 15:29
  • 1
    Pretty sure that is not true, as it also gives this as an open: "OR add MockK Java Agent instrumentation to make all classes 'open'". I believe that MockK will make all classes open for testing. – lostintranslation May 11 '23 at 15:32
  • 1
    Why do you want to test a private method? Before even think of using Reflection or Mockito to make it callable, please refactor your code towards testability. If it's not your code, then you should accept it as "black box" and use the public methods only. – oliver_t May 16 '23 at 13:04
  • @oliver_t, not my code, trying to get around something at the moment. Hence my comment in the question that I fully understand the implication. – lostintranslation May 16 '23 at 14:27

2 Answers2

1

As outlined in the documentation, MockK supports spying on and mocking private functions.

Just be sure to understand the implications of testing private methods of a class.

Let's say you use an 3rd party library you cannot control with a class Foo, which provides a method publicMethod which in turn calls a private method privateMethod and you want to mock the latter to ease testing.

class Foo {
  fun publicMethod(): String = "Hello ${privateMethod()}!"

  private fun privateMethod(): String = "World"
}

You can mock the privateMethod with something along the lines of:

fun `mock private function`() {
  val mock = spyk<Foo>(recordPrivateCalls = true)

  every { mock["privateMethod"]() } returns "Test"

  assertEquals("Hello Test!", mock.publicMethod())
}
Endzeit
  • 4,810
  • 5
  • 29
  • 52
  • So I can mock the private method, but not call it directly? – lostintranslation May 22 '23 at 16:02
  • @lostintranslation If you want to actually call the private function yourself, you most likely will need to make use of reflection. I'd recommend to take a look at this [question / answer](https://stackoverflow.com/a/72111931/3160089) for a way how to achieve that. – Endzeit May 23 '23 at 06:08
0

you can try this please...

 when(spy, method(CodeWithPrivateMethod.class, "privateMethod", String.class, int.class)) 
.withArguments(anyString(), anyInt()) 
.thenReturn(true);

or

    doReturn(true).when(codeWithPrivateMethod, "privateMethod", anyString(), anyInt());

or With no argument:

ourObject = PowerMockito.spy(new OurClass());
when(ourObject , "ourPrivateMethodName").thenReturn("mocked result");

or With String argument:

ourObject = PowerMockito.spy(new OurClass());
when(ourObject, method(OurClass.class, "ourPrivateMethodName", String.class))
                .withArguments(anyString()).thenReturn("mocked result");