如何在androidTest中使用Mockito?

18

在我的测试中,我如何模拟我写的一个类?

以下是我的测试代码:

import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

@RunWith(AndroidJUnit4.class)
public class SampleTest {
    @Mock
    Sample mySample;

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

    @Test
    public void shouldMockSample() {
        verify(mySample, never()).neverCallMe();
    }

}

Sample只是一个虚拟类。

public class Sample {

    public void neverCallMe() {}
}

我该如何消除这个错误?

java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167) at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217) at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109) at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105) at org.mockito.cglib.proxy.Enhancer.(Enhancer.java:70) at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49) at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24) at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33) at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59) at org.mockito.Mockito.spy(Mockito.java:1368) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) 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.RunBefores.evaluate(RunBefores.java:24) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) 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.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:54) at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:228) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1741)
java.lang.NoClassDefFoundError: org/mockito/internal/creation/cglib/ClassImposterizer$3 at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:95) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:57) at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:49) at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24) at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33) at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59) at org.mockito.Mockito.spy(Mockito.java:1368) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run

我在我的gradle文件中使用了以下依赖项:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.1'
    androidTestCompile 'com.android.support:support-annotations:22.2.1'
    androidTestCompile 'org.mockito:mockito-core:1.10.19'
    androidTestCompile 'org.objenesis:objenesis:2.1'
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    androidTestCompile 'com.android.support.test:runner:0.3'
    androidTestCompile 'com.android.support.test:rules:0.3'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
    testCompile 'org.mockito:mockito-core:1.10.19'
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    testCompile 'junit:junit:4.12'
    testCompile 'org.robolectric:robolectric:3.0'
}
3个回答

35

自版本2.6.+起,Mockito增加了一个新的构件,可在Android上工作,无需任何其他依赖项(即不再需要导入DexMaker)。(参考)

只需将org.mockito:mockito-android作为您的仪器化单元测试( androidTest )的依赖项即可。 对于本地单元测试,仍会使用通常的 org.mockito:mockito-core

示例:

dependencies {
    ...
    testImplemenation 'org.mockito:mockito-core:2.7.15'
    androidTestImplementation 'org.mockito:mockito-android:2.7.15'
    ...
}

我已经做了这个,但仍然遇到问题。 - filthy_wizard
1
使用androidTestImplementation代替androidTestCompile,因为它已经被弃用。 - E.T.
谢谢,这对我们的情况也适用! - mochadwi

16

默认情况下,Mockito使用CGLib或ByteBuddy,两者都会生成.class文件。由于您正在运行Android设备或模拟器,所以.class文件无法帮助您;您需要使用.dex格式。

调整您的依赖项以使用DexMaker,它将覆盖Mockito的默认设置,并允许在Android环境中进行模拟。


非常感谢!添加dexmaker使我通过了错误和样本测试。 - ericharlow
你有没有遇到过 @Mock 无法工作但是 mock(object) 可以的情况? - ericharlow
@ericharlow 我有过类似的经历,但并没有使用你明确提到的 MockitoAnnotations.initMocks(this)。你是否仍然采用这种方式,或者已经重构为使用 MockitoRule 或 MockitoJUnitRunner 了呢?(你可能需要另外发一个问题来询问。) - Jeff Bowman
在尝试初始化新的测试支持库中的模拟对象时,我遇到了错误。出现了java.lang.ExceptionInInitializerError异常,不确定原因。 - filthy_wizard

0

添加这些依赖项

androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1")

testImplementation("junit:junit:4.13.2")
testImplementation("androidx.test:core:1.4.0")
testImplementation("org.mockito:mockito-android:4.2.0")
testImplementation("org.mockito:mockito-inline:4.2.0")
testImplementation("org.mockito:mockito-core:4.2.0")

你可以像这样使用mockito:

@RunWith(AndroidJUnit4::class)
class MainActivityTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    private lateinit var viewModel: HomeScreenViewModel

    @Mock
    private val sampleUseCase = mock(SampleUseCase::class.java)

    @Before
    fun setup() {
        runBlockingTest {
            `when`(sampleUseCase.invoke(anyInt(), anyString())).thenReturn(
                NetworkDataState.Success(listOf(
                    SampleObject(id = 1),
                    SampleObject(id = 2),
                    SampleObject(id = 3),
                ))
            )

            viewModel = HomeScreenViewModel(
                sampleUseCase
            )
        }
    }

    @Test
    fun navigationTest() {
        composeTestRule.setContent {
            AppTheme {
                MainScreen(
                    navController = rememberNavController(),
                    viewModel = viewModel
                )
            }
        }

        composeTestRule
            .onNodeWithContentDescription("chips")
            .performScrollToIndex(4)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接