Search code examples
androidauth0junit5mockkrx-java3

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


We upgraded our Auth0 Android library from 1.30 to 2.0 and some of our unit tests that use OkHttp's MockWebServer broke.

  1. The first test fails with a java.lang.ExceptionInInitializerError which in turn is caused by java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.
  2. Next three tests fail with java.lang.NoClassDefFoundError: Could not initialize class com.auth0.android.request.internal.DefaultThreadSwitcher
  3. The rest of the tests are stuck until cancelled.

For context, we use RxJava3 and Android Jetpack libs such as LiveData in our code base, and Retrofit for REST API.

I tried adding @ExtendWith(InstantExecutorExtension::class, RxImmediateScheduler::class) in the class declaration like this:

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

But afterwards, all tests return with "Test ignored" in the logs:

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$1(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$5(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$7(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$8(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$0(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)

Here is a code snippet on how we initialize the test 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()
        }
...
}

Here is the first test that fails:

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

Solution

  • Already fixed with version 2.3.0 of Auth0 android lib--see the README on how to solve this issue here