ChatGPT解决这个技术问题 Extra ChatGPT

如何使用 mockito 捕获特定类型的列表

有没有办法使用 mockitos ArgumentCaptore 捕获特定类型的列表。这不起作用:

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
我发现在这里使用具体的列表实现是一个糟糕的主意(ArrayList)。您始终可以使用 List 接口,如果您想表示它是协变的事实,那么您可以使用 extendsArgumentCaptor<? extends List<SomeType>>

s
shiramy

使用 @Captor annotation 可以避免嵌套泛型问题:

public class Test{

    @Mock
    private Service service;

    @Captor
    private ArgumentCaptor<ArrayList<SomeType>> captor;

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

    @Test 
    public void shouldDoStuffWithListValues() {
        //...
        verify(service).doStuff(captor.capture()));
    }
}

我更喜欢在 @Before 方法中使用 MockitoAnnotations.initMocks(this),而不是使用排除使用另一个运行器的能力的运行器。但是,+1,感谢您指出注释。
不确定这个例子是否完整。我得到......错误:(240、40)java:变量捕获器可能没有被初始化我喜欢下面的tenshi的回答
我遇到了同样的问题,发现这篇博文对我有所帮助:blog.jdriven.com/2012/10/…。它包括一个在您将注释放在类上之后使用 MockitoAnnotations.initMocks 的步骤。我注意到的一件事是你不能在局部变量中使用它。
在 Captor 声明中,我通常更喜欢 List 而不是 ArrayList: ArgumentCaptor> captor;
initMocks(this) 应替换为 openMocks(this),因为第一个已弃用
J
Jacob van Lingen

是的,这是一个通用的泛型问题,而不是特定于模拟的。

ArrayList<SomeType> 没有类对象,因此您不能以类型安全的方式将此类对象传递给需要 Class<ArrayList<SomeType>> 的方法。

您可以将对象转换为正确的类型:

Class<ArrayList<SomeType>> listClass =
              (Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);

这将给出一些关于不安全强制转换的警告,当然,如果不检查元素,您的 ArgumentCaptor 无法真正区分 ArrayList<SomeType>ArrayList<AnotherType>

(正如另一个答案中提到的,虽然这是一个通用的泛型问题,但对于 @Captor 注释的类型安全问题,有一个 Mockito 特定的解决方案。它仍然无法区分 ArrayList<SomeType> 和 { 3}。)

编辑:

另请查看 tenshi 的评论。您可以将原始代码更改为此简化版本:

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);

基于 java 对静态方法调用进行类型推断这一事实,您展示的示例可以简化:ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
要禁用 uses unchecked or unsafe operations 警告,请使用参数捕获器定义行上方的 @SuppressWarnings("unchecked") 注释。此外,强制转换为 Class 是多余的。
在我的测试中,转换为 Class 并不是多余的。
M
Manoj Shrestha

如果你不害怕旧的 java 风格(非类型安全的泛型)语义,这也可以工作并且很简单:

ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject).method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.

您可以在声明之前添加 @SuppressWarnings("rawtypes") 以禁用警告。
A
Ahmed Ashour
List<String> mockedList = mock(List.class);

List<String> l = new ArrayList();
l.add("someElement");

mockedList.addAll(l);

ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);

verify(mockedList).addAll(argumentCaptor.capture());

List<String> capturedArgument = argumentCaptor.<List<String>>getValue();

assertThat(capturedArgument, hasItem("someElement"));

m
mrts

基于@tenshi 和@pkalinow 的评论(也感谢@rogerdpack),以下是创建列表参数捕获器的简单解决方案,它还禁用“使用未经检查或不安全的操作”警告:

@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
    ArgumentCaptor.forClass(List.class);

完整示例 here 和相应的通过 CI 构建和测试运行 here

我们的团队已经在我们的单元测试中使用了一段时间,这对我们来说似乎是最直接的解决方案。


q
quzhi65222714

对于较早版本的junit,您可以执行

Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);

T
Timofey Orischenko

我在我的 Android 应用程序中测试活动时遇到了同样的问题。我使用了 ActivityInstrumentationTestCase2,而 MockitoAnnotations.initMocks(this); 没有用。我用另一个类分别解决了这个问题。例如:

class CaptorHolder {

        @Captor
        ArgumentCaptor<Callback<AuthResponse>> captor;

        public CaptorHolder() {
            MockitoAnnotations.initMocks(this);
        }
    }

然后,在活动测试方法中:

HubstaffService hubstaffService = mock(HubstaffService.class);
fragment.setHubstaffService(hubstaffService);

CaptorHolder captorHolder = new CaptorHolder();
ArgumentCaptor<Callback<AuthResponse>> captor = captorHolder.captor;

onView(withId(R.id.signInBtn))
        .perform(click());

verify(hubstaffService).authorize(anyString(), anyString(), captor.capture());
Callback<AuthResponse> callback = captor.getValue();

J
Jezor

关于这个确切的问题有一个open issue in Mockito's GitHub

我找到了一个简单的解决方法,它不会强迫您在测试中使用注释:

import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;

public final class MockitoCaptorExtensions {

    public static <T> ArgumentCaptor<T> captorFor(final CaptorTypeReference<T> argumentTypeReference) {
        return new CaptorContainer<T>().captor;
    }

    public static <T> ArgumentCaptor<T> captorFor(final Class<T> argumentClass) {
        return ArgumentCaptor.forClass(argumentClass);
    }

    public interface CaptorTypeReference<T> {

        static <T> CaptorTypeReference<T> genericType() {
            return new CaptorTypeReference<T>() {
            };
        }

        default T nullOfGenericType() {
            return null;
        }

    }

    private static final class CaptorContainer<T> {

        @Captor
        private ArgumentCaptor<T> captor;

        private CaptorContainer() {
            MockitoAnnotations.initMocks(this);
        }

    }

}

这里发生的情况是,我们创建了一个新类 with @Captor 注释并将捕获器注入其中。然后我们只需提取捕获者并从我们的静态方法中返回它。

在您的测试中,您可以像这样使用它:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());

或者使用类似于 Jackson 的 TypeReference 的语法:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
    new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
    }
);

它可以工作,因为 Mockito 实际上不需要任何类型信息(例如,与序列化程序不同)。