涉及 MockWebServer 的单元测试因 Auth0 的 Android 库版本 2 而失败

Unit tests involving MockWebServer fails with Auth0's Android library version 2

我们将 Auth0 Android 库从 1.30 升级到 2.0,我们的一些使用 OkHttp MockWebServer 的单元测试失败了。

  1. 第一个测试失败并显示 java.lang.ExceptionInInitializerError,这又是由 java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.
  2. 引起的
  3. 接下来的三个测试失败 java.lang.NoClassDefFoundError: Could not initialize class com.auth0.android.request.internal.DefaultThreadSwitcher
  4. 其余的测试会一直卡住,直到被取消。

对于上下文,我们在我们的代码库中使用 RxJava3 和 Android Jetpack 库,例如 LiveData,以及 Retrofit for REST API.

我试过添加 @ExtendWith(InstantExecutorExtension::class, RxImmediateScheduler::class) 在 class 声明中是这样的:

@ExtendWith(InstantExecutorExtension::class, RxImmediateScheduler::class)
class AuthManagerTest {

但之后,所有测试 return 在日志中显示“忽略测试”:

Test ignored.

java.lang.InstantiationException
    at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.junit.platform.commons.util.ReflectionUtils.newInstance(ReflectionUtils.java:513)
    at org.junit.platform.commons.util.ReflectionUtils.newInstance(ReflectionUtils.java:488)
    at org.junit.jupiter.engine.extension.MutableExtensionRegistry.registerExtension(MutableExtensionRegistry.java:176)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryFrom(MutableExtensionRegistry.java:117)
    at org.junit.jupiter.engine.descriptor.ExtensionUtils.populateNewExtensionRegistryFromExtendWithAnnotation(ExtensionUtils.java:77)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.prepare(ClassBasedTestDescriptor.java:143)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.prepare(ClassBasedTestDescriptor.java:78)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare(NodeTestTask.java:111)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:111)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:79)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute[=12=](EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

这是我们如何初始化测试的代码片段 class:

class AuthManagerTest {

    ...
    private lateinit var authManager: AuthManager
    private lateinit var config: Auth0Config
    private lateinit var server: MockWebServer

    @MockK
    private lateinit var context: Context

    @MockK
    private lateinit var credentialsManager: SecureCredentialsManager

    ...

    ...

    @BeforeEach
    fun setup() {
        MockKAnnotations.init(this)
        every { context.getSharedPreferences(any(), any()) } returns mockk()
        every { credentialsManager.authToken } returns null
        every { credentialsManager.userId } returns null
        every { credentialsManager.refreshToken } returns null
        every { credentialsManager.saveCredentials(any()) } just Runs

        ...

        mockkObject(Credentials.Companion)
        every { Credentials.fromAuth0Creds(any(), any()) } returns mockk()
        server = MockWebServer().apply {
            // we didn't use useHttps before but with the new version of Auth0 it seems https is mandatory
            useHttps(testSslSocketFactory(), false)
            start()
        }
...
}

这是第一个失败的测试:

    @Test
    fun `sendEmailCode returns EmailSent when response is successful`() {
        server.enqueue(mockResponse(200, email_success_response))
        val emailResult = authManager.sendEmailCode("me@example.org").blockingGet()
                as EmailResult.EmailSent
        assertEquals("me@example.org", emailResult.email)
        assertNoToken()
    }

已通过 Auth0 android lib 的 2.3.0 版修复——请参阅有关如何解决此问题的自述文件 here