【问题标题】:Mockito Uri.parse always returns nullMockito Uri.parse 总是返回 null
【发布时间】:2015-08-16 13:36:45
【问题描述】:

我有这个制作方法:

public boolean onShouldOverrideUrlLoading(String url) {
    boolean isConsumed = false;
    if (url.contains("code=")) {
         Uri uri = Uri.parse(url);
         String authCode = uri.getQueryParameter("code");
         mView.authCodeObtained(authCode);
         isConsumed = true;
    }
    return isConsumed;
}

我有这个 Mockito 测试方法:

@Test
public void onShouldOverrideUrlLoadingOnAuthCodeObtained(){

    String code = "someCode";

    boolean isConsumed = mPresenter.onShouldOverrideUrlLoading("http://localhost/?code=" + code);

    verify(mView, times(1)).authCodeObtained(code);
    assertEquals(isConsumed, true);
}

但似乎一旦代码运行并到达 Uri.parse(url),我得到一个空指针。我错过了什么?在生产中,这非常有效。只有在测试时,Uri.parse() 才会返回 null。

谢谢!

【问题讨论】:

  • 问题可能出在 Uri 中,可能在测试环境中初始化错误。我们Uri 不存在于Android SDK 中URI 存在。所以它看起来不像是一个交付的类,这可能是问题所在。与 mockito 无关。
  • @Brice 谢谢。是的,这似乎是问题所在。您应该将此作为答案发布。 Uri 类来自 Android。所以 Mockito 以这种方式使用它会遇到一些困难。
  • 完成 :) 希望有所帮助。
  • @Alon 您是如何准确解决问题的?如何在 Android 中正确初始化我的测试环境?

标签: android unit-testing testing mocking


【解决方案1】:

最终我在 Mockito 之上使用 PowerMock 来模拟 Uri 类。 使用这些依赖项来添加它:

'org.powermock:powermock-api-mockito:1.4.12'
'org.powermock:powermock-module-junit4:1.6.2'

阅读here。

它使您能够模拟静态方法、私有方法、最终类等。 在我使用的测试方法中:

PowerMockito.mockStatic(Uri.class);
Uri uri = mock(Uri.class);

PowerMockito.when(Uri.class, "parse", anyString()).thenReturn(uri);

这通过了实际代码中的行并返回了一个 Mocked Uri 对象,因此测试可以继续进行。 确保添加:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Uri.class})

作为测试类名之上的注解。

使用 Robolectric 来解决这个问题也是一种有效的技术。

希望这能有所帮助。

【讨论】:

    【解决方案2】:

    我通过使用 Roboelectric 测试运行器解决了这个问题。

    @RunWith(RobolectricGradleTestRunner::class)
    @Config(constants = BuildConfig::class, sdk = intArrayOf(19))
    class WeekPageViewModelConverterTests {
    ...
    }
    

    【讨论】:

    • 我用 Java 写这个的等效方法是:@RunWith(RobolectricTestRunner.class)public class ExampleUnitTest {
    【解决方案3】:

    这里的问题是用于运行单元测试的android.jar 文件不包含任何实际代码。 这样android.net.Uri 将默认返回null 并使用

    android {
      // ...
      testOptions { 
        unitTests.returnDefaultValues = true
      }
    }
    

    不会解决这种情况下的问题,因为方法的默认返回值仍然是null

    我的解决方案是使用Unmock 插件,其唯一目的是从android.jar 中取消模拟一些类/包并为它们提供完整的实现。

    在这种特定情况下,只需将 sn-p 添加到您的 build.gradle 文件中

    apply plugin: 'de.mobilej.unmock'
     //...
    unMock {
        keepStartingWith "android.net.Uri"
    }
    

    【讨论】:

    • 这行得通,但我必须提供额外的保留规则 unMock { keep "android.net.Uri" keepAndRename "java.nio.charset.Charsets" to "xjava.nio.charset.Charsets" keepStartingWith “libcore”。 }
    【解决方案4】:

    问题可能出在Uri 类,因为被测试的代码是静态代码(Uri.parse(...)),Uri 可能在测试环境中初始化错误。请注意,Android SDK 中有两个 uri 类:

    我不是 Android 开发人员,但您可能想检查一下测试环境。

    【讨论】:

    • 您是如何准确解决问题的?如何在 Android 中正确初始化我的测试环境?
    • @user59442 我不能回答你这个问题,我不是 Android 开发人员。而是问问题的作者 Alon Minski!
    【解决方案5】:

    我通过使用“@RunWith(RobolectricTestRunner::class)”运行测试套件解决了这个问题

    【讨论】:

    • 当我添加这个库时,我的测试将继续构建几分钟,我再也看不到结果了
    • 终于成功了,在我的情况下,第一次建造会持续很长时间,将近 20 分钟。感谢这种方法............
    【解决方案6】:

    我在使用 android.net.Uri 类进行测试时遇到了类似的问题。

    我已经解决了将测试从“test”文件夹移动到“androidTest”并在设备中运行的问题。在设备中,Uri 类行为正常。

    【讨论】:

      【解决方案7】:

      我能够使用android.net.Uri 使用AndroidJUnit4 runner,如下所示

      import androidx.core.net.toUri
      import androidx.test.ext.junit.runners.AndroidJUnit4
      ...
      @RunWith(AndroidJUnit4::class)
      class FileTest {
        @get:Rule(order = 0)
        val tempFolder = TemporaryFolder()
        private lateinit var tempFile: File
      
        @Before
        fun setup() {
          tempFile = tempFolder.newFile()
        }
      
        @Test
        fun test_fileUri_not_null() {
          assertTrue(tempFile.exists())
          assertNotNull(tempFile.toUri())
        }
      }
      

      上面的测试写在tests目录下

      【讨论】:

      • tempFile.toUri() 出现错误:无法将使用 JVM 目标 1.8 构建的字节码内联到使用 JVM 目标 1.6 构建的字节码中。请指定正确的“-jvm-target”选项
      【解决方案8】:

      如果您使用 android.net.Uri 类,那么您需要在 Mockito 之上使用 Robolectric (http://robolectric.org) 运行测试

      【讨论】:

        【解决方案9】:

        在使用powermockito 时,唯一对我有用的解决方案如下。

        PowerMockito.mockStatic(Uri.class);
        Uri uri = mock(Uri.class);
        when(uri.getLastPathSegment()).thenReturn("path");
        
        when(Uri.parse(anyString())).thenAnswer((Answer<Uri>) invocation -> uri);
        

        在这里,我们可以设置自定义值,由uri对象的各个函数返回。

        【讨论】:

          猜你喜欢
          • 2022-12-18
          • 1970-01-01
          • 1970-01-01
          • 2015-07-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-13
          相关资源
          最近更新 更多