15

in Android I've been using the version 1.6.1 of Powermock and all this implementation worked really good for statics. It's not working now at all when I changed to 2.0.0-beta.5. Indeed, it didn't even work upgrading from my previous 1.6.1 to 1.7.1.

I have this implementation:

// Power Mockito
testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
testImplementation "org.powermock:powermock-module-junit4-rule-agent:2.0.0-beta.5"
testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
//testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.0-beta.5'

// Mockito
testImplementation "org.mockito:mockito-core:2.11.0"
testImplementation "com.nhaarman:mockito-kotlin-kt1.1:1.5.0"
androidTestImplementation("com.nhaarman:mockito-kotlin-kt1.1:1.5.0", {
    exclude group: 'org.mockito', module: 'mockito-core'
})
androidTestImplementation 'org.mockito:mockito-android:2.11.0'

and I'm trying to mock a static the same way I was doing with 1.6.1:

@RunWith(PowerMockRunner::class)
@PrepareForTest(SharedPreferencesHelper.Companion::class, ConfigUseCaseTests::class)
class ConfigUseCaseTests {

    lateinit var context: Context

    @Before
    fun setUp() {
        context = mock()
    }

    @Test
    fun getConfigs_fromJson() {
        PowerMockito.mockStatic(SharedPreferencesHelper.Companion::class.java)

        val instance = mock<SharedPreferencesHelper.Companion>()
        doReturn("foo")
                .whenever(instance)
                .loadString(isA(), anyString(), anyString(), anyString())
//        whenever(instance.loadString(isA(), anyString(), anyString(), anyString())).thenReturn("foo") // This shows the same error
        PowerMockito.whenNew(SharedPreferencesHelper.Companion::class.java)
                .withAnyArguments()
                .thenReturn(instance)

        val mockedFoo = instance.loadString(context, "", "", "") // This shows "foo"

        val mockedCompanion = SharedPreferencesHelper.loadString(context, "", "", "") // This is throwing NullPointerException

        Assert.assertEquals(mockedCompanion, "foo")
    }
}

My SharedPreferencesHelper looks like:

class SharedPreferencesHelper {
    companion object {

        @Suppress("NON_FINAL_MEMBER_IN_OBJECT")
        open fun loadString(context: Context, fileName: String, key: String, defaultValue: String?): String? {
            val sharedPreferences = getWithFileName(context, fileName)
            return if (!sharedPreferences.contains(key)) {
                defaultValue
            } else {
                sharedPreferences.getString(key, defaultValue)
            }
        }
    }
}

I've tried to play with the open but it didn't work.

Exception: (I don't understand it at all)

java.lang.NullPointerException
    at my.package.ConfigUseCaseTests.getConfigs_fromJson(ConfigUseCaseTests.kt:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:326)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)

I can say, sometimes IT WORKS!! I'm adding the video because it looks amazing that it happens just sometimes: https://youtu.be/YZObVLcERBo (watch at the middle and the end)

Rafael Ruiz Muñoz
  • 5,333
  • 6
  • 46
  • 92
  • 1
    I know you will not like my statement because it does not solve the problem the way you want to, but that's why I am posting it as a comment. Maybe you want to consider the advice of someone who has been coaching developers for many years, also about test automation and making applications more testable: **Don't use PowerMock, it is a code smell!** Whenever you feel the urge to use it, it is a clear sign that instead you should refactor your code for testability. If I would look into your question in order to solve it with PowerMock, then only from a "sports" perspective as a finger exercise. – kriegaex Jun 23 '18 at 02:56
  • @Rafael Ruiz Muñoz, You got any solution for this?? – bGorle Jan 05 '20 at 12:29
  • @bGorle yes, migrating to MockK. The easiest option. – Rafael Ruiz Muñoz Jan 05 '20 at 18:14
  • Thanks for your reply, Wrote some tests with mockk initially tests are working after some time they were not working, Then tried to mock with powermock mockstatic. Removed the powermock-agent dependency now mockk is working. – bGorle Jan 06 '20 at 06:44

1 Answers1

1

The way companion objects are created on compilation is by creating a static field inside the surrounding class. It get's instantiated on the static scope (before the test is instantiated).

This is how it looks when decompiled in Java:

public final class SharedPreferencesHelper {
  public static final SharedPreferencesHelper.Companion Companion = new 
  SharedPreferencesHelper.Companion((DefaultConstructorMarker)null);
  // ...
}

For this to work you'll have to assign the given field with your mock instead of intercepting the creation of the Companion object. This doesn't even require to use PowerMock and can be done with reflexion: https://dzone.com/articles/how-to-change-private-static-final-fields

pablisco
  • 14,027
  • 4
  • 48
  • 70
  • 1
    Thanks for the answer, but this answer seems to be more like the definition of a Static/Companion class in Kotlin. Indeed I'm saying I want to mock the Companion by `PowerMockito.mockStatic(SharedPreferencesHelper.Companion::class.java)` and of course I'm telling PowerMockito to mock the constructor with any arguments ` PowerMockito.whenNew(SharedPreferencesHelper.Companion::class.java)` – Rafael Ruiz Muñoz Jun 08 '18 at 12:43