首页 > 解决方案 > 获取 Mockito 错误:“需要但未调用...实际上,与此模拟的交互为零”

问题描述

我在 Android 项目(在 Android Studio 中)中为 java 类运行单元测试时遇到上述错误。

被测类:

import android.content.Context;
import android.util.Log;

import **.CustomObject;
import java.util.concurrent.CountDownLatch;
import androidx.annotation.NonNull;

public class CustomClass {
  private static final String string = "a";
  private static CustomObject customObject = null;
  private static CountDownLatch initializedLatch = new CountDownLatch(1);

  @NonNull
  public static CustomObject1 getCustomObject1() {
      try {
          initializedLatch.await();
          assert customObject != null;
          return customObject;

      } catch (InterruptedException e) {
        throw new RuntimeException(".");
      }
  }

  public static void methodA(final Context context,
                                         final String string1,
                                         ) throws exception {
      initializedLatch.countDown();
  }


  public static void methodB(@NonNull final CustomObject customObjectInput) {
      customObject = customObjectInput;
  }
}

测试类:

import android.content.Context;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.util.concurrent.CountDownLatch;

import **.CustomObject;

import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

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

@RunWith(MockitoJUnitRunner.class)
public class CustomClassTest{

    @Mock
    static CustomObject customObject;

    @Mock
    static Context context;

    @Mock
    CountDownLatch mCountDownLatch;

    @Mock
    CountDownLatch mInitializedLatch;

    @InjectMocks
    CustomClass customClass;

    @Before
    public void setUp() {
        customObject = Mockito.spy(CustomObject.class);
        context = Mockito.spy(Context.class);
    }

    @Test
    public void customClassTest() {

        doNothing().when(mInitializedLatch).countDown();

        CustomClass.methodB(customObject);
        try {
            CustomClass.methodA(context, "");
        } catch (Exception e) {
            e.printStackTrace();
        }
        verify(mInitializedLatch).countDown();

        try {
            doNothing().when(mInitializedLatch).await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Class.getCustomObject();
    }

我在运行 customClassTest 时收到的具体消息:

Wanted but not invoked:
mInitializedLatch.countDown();
-> at CustomClassTest.methodA(CustomClassTest.java:79)
Actually, there were zero interactions with this mock.

Wanted but not invoked:
mInitializedLatch.countDown();
-> at CustomClassTest.methodA(CustomClassTest.java:79)
Actually, there were zero interactions with this mock.

在每个相关行处运行带有断点的调试器似乎表明测试运行良好(所有变量都在正确的点正确分配)直到 verify(mInitializedLatch).countDown();,当消息出现时(并且代码停止运行)。

任何帮助表示赞赏,谢谢。

更新#1:

更改代码以删除静态关键字:

import android.content.Context;
import android.util.Log;

import **.CustomObject;
import java.util.concurrent.CountDownLatch;
import androidx.annotation.NonNull;

public class CustomClass {
  private final String string = "a";
  private CustomObject customObject = null;
  private CountDownLatch initializedLatch = new CountDownLatch(1);

  @NonNull
  public CustomObject1 getCustomObject1() {
      try {
          initializedLatch.await();
          assert customObject != null;
          return customObject;

      } catch (InterruptedException e) {
        throw new RuntimeException(".");
      }
  }

  public void methodA(final Context context,
                                         final String string1,
                                         ) throws exception {
      initializedLatch.countDown();
  }


  public void methodB(@NonNull final CustomObject customObjectInput) {
      customObject = customObjectInput;
  }
}
import android.content.Context;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.util.concurrent.CountDownLatch;

import **.CustomObject;

import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

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

@RunWith(MockitoJUnitRunner.class)
public class CustomClassTest{

    @Mock
    CustomObject customObject;

    @Mock
    Context context;

    @Mock
    CountDownLatch mCountDownLatch;

    @Mock
    CountDownLatch mInitializedLatch;

    @InjectMocks
    CustomClass customClass;

    @Before
    public void setUp() {
        customObject = Mockito.spy(CustomObject.class);
        context = Mockito.spy(Context.class);
    }

    @Test
    public void customClassTest() {

        doNothing().when(mInitializedLatch).countDown();

        customClass.methodB(customObject);
        try {
            customClass.methodA(context, "");
        } catch (Exception e) {
            e.printStackTrace();
        }
        verify(mInitializedLatch).countDown();

        try {
            doNothing().when(mInitializedLatch).await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        customClass.getCustomObject();
    }

错误消息现在阅读:

error: non-static method methodA(Context,String) cannot be referenced from a static context
error: non-static method getCustomObject1() cannot be referenced from a static context

第二条错误消息显示六次。似乎代码没有编译。

标签: javaandroidtestingmockito

解决方案


在 CustomClass 中,CountDownLatch 被声明为静态字段并被初始化。如果你调试你的类,你可以看到 Mockito 没有模拟/代理这个字段。所有与 initializedLatch 对象的代码交互都不会被 Mockito 代理拦截,所以当你通过 doNothing().when(mInitializedLatch).countDown() 设置测试时,实际上你并没有将字段设置为 customClass。所以当你使用 verify(mInitializedLatch).countDown(),您实际上是在对 Mockito 说您希望与此模拟进行一次交互,但由于上述原因,没有进行交互。


推荐阅读