【问题标题】:Mockito on Android, Context.getString(id) and NullPointerExceptionAndroid 上的 Mockito、Context.getString(id) 和 NullPointerException
【发布时间】:2016-10-27 02:40:53
【问题描述】:

我刚开始学习一个 Mockito 测试框架,我按照官方教程从:developer.android.com

代码是:

private static final String FAKE_STRING = "HELLO WORLD";

@Mock
Context mMockContext;

@Test
public void readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    when(mMockContext.getString(R.string.hello_world))
            .thenReturn(FAKE_STRING);
    ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext);

    // ...when the string is returned from the object under test...
    String result = myObjectUnderTest.getHelloWorldString();

    // ...then the result should be the expected one.
    assertThat(result, is(FAKE_STRING));
}

我写了以下ClassUnderTest:

public class ClassUnderTest {

private Context context;

public ClassUnderTest(Context context)
{
    this.context=context;
}

public String getHelloWorldString()
{
    return context.getString(R.string.hello_world);
}

}

运行后我得到 NullPointerException:

    java.lang.NullPointerException
at android.content.Context.getHelloWorldString(Context.java:293)
at com.darekk.draggablelistview.unit_tests.MockitoTest.readStringFromContext_LocalizedString(MockitoTest.java:46)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1619)

当我用我自己的类替换 android Context 时,它可以工作。

平台:IntelliJ IDEA 社区版 15.0.6

非常感谢任何帮助。

【问题讨论】:

    标签: android unit-testing testing mockito


    【解决方案1】:

    这段代码应该是你测试的第一行:

    doReturn("Sample Hello world string")
                .when(mMockContext)
                .getString(any(Integer.class));
    

    需要一个方法的存根

    【讨论】:

    • 嗯,我应该把它放在 getHelloWorldString() 方法中吗?究竟在哪里?,很奇怪。我认为只有 Context.getString(...) 应该被存根。
    • 编辑了我的答案。无需修改被测类。
    • 我有这个:when(mMockContext.getString(R.string.hello_world)) .thenReturn(FAKE_STRING);
    • 我尝试了你的解决方案,但是 any(Integer.class) 无法解析,所以我使用了 anyInt() 并得到了另一个错误:org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid Use of argument匹配器!预期 0 个匹配器,记录 1 个:
    • 你的解决方案和发帖者已经拥有的没有区别when(mMockContext.getString(R.string.hello_world)).thenReturn(FAKE_STRING);
    【解决方案2】:

    您似乎使用的是来自developer.android.com 的确切代码。显然他们发布了一个不完整的示例..将此方法添加到您的测试中:

    @Before
    public void setup(){
       MockitoAnnotations.initMocks(this);
    }
    

    更多参考结帐Mockito's docs

    【讨论】:

      【解决方案3】:

      Context.getString() 是最终方法,因此默认情况下您不能使用 Mockito 模拟它。 要启用最终方法的模拟,您需要 Mockito v2 并按照此处的说明进行操作 Mock the unmockable: opt-in mocking of final classes/methods

      src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 中创建一个包含一行的文件:

      mock-maker-inline
      

      一旦你这样做了,你就可以模拟 getString() when(mMockContext.getString(R.string.string_name)).thenReturn("Mocked String");

      【讨论】:

      • 这是不正确的。根据 Android 文档:“在运行时,[本地单元] 测试针对 android.jar 的修改版本执行,其中所有最终修饰符都已被剥离。这使您可以使用流行的模拟库,例如 Mockito。”见developer.android.com/studio/test/…
      【解决方案4】:

      在您的 build.gradle 文件中添加以下依赖项

      testImplementation "org.powermock:powermock:1.6.5"
      

      运行上面的代码,它将开始工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-07-27
        • 2017-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多