如何在Java的Mockito中为Map对象创建参数捕获器?

11

如何为 Map<String, SomeCustomClass> 创建一个参数捕获器?

我有一段遵循以下模式的代码:

import java.util.HashMap;
import java.util.Map;

public class CompoundClass {

   public CompoundClass (String a, String b){
       this.a = a;
       this.b = b;
   }

   public String a;
   public String b;
}

public class SubClass {
    public void doSomeThingSubClass(Map<String, CompoundClass> mapSb) {
        ...
    }
}

public class Example {

    public SubClass sb;

    public Example(SubClass sb) {
        this.sb = sb;
    }

    public void doSomeThing () {
        Map<String, CompoundClass> mapSb = new HashMap<>();
        mapSb.put("x", new CompoundClass("aa","bb"));
        sb.doSomeThingSubClass(mapSb);
    }
}

我想测试方法doSomethingSubClass(mapSb)是否被调用,我需要能够检查它使用了什么参数。为此,我有以下单元测试:

@Test
void TestDoSomehing(){
    SubClass sb = mock(SubClass.class);
    
    Example ex = new Example(sb);
    
    ArgumentCaptor<Map<String, CompoundClass>> argCaptor = ArgumentCaptor.forClass(Map<String, CompoundClass>.class);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());
    
    System.out(argCaptor.getValue().get('x').a);
}

问题在于上述对argCaptor的初始化会产生以下错误信息:"Cannot select from parametrized type"。因此,问题是如何正确声明和初始化一个类似于Map<String, SomeCustomeClass>的map对象的argument captor?谢谢!
2个回答

17
你可以选择以下方式之一:

使用 @SuppressWarnings("unchecked")

  @Test
  @SuppressWarnings("unchecked")
  void TestDoSomething(){
    SubClass sb = mock(SubClass.class);

    Example ex = new Example(sb);

    ArgumentCaptor<Map<String, CompoundClass>> argCaptor = ArgumentCaptor.forClass(Map.class);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());

    System.out.println(argCaptor.getValue().get("x").a);
  }

或使用junit5和@Captor注释:

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

import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
@TestInstance(Lifecycle.PER_METHOD)
public class TestDoSomething {

  @Captor
  private ArgumentCaptor<Map<String, CompoundClass>> argCaptor;

  @Test
  void TestDoSomething2(){
    SubClass sb = mock(SubClass.class);

    Example ex = new Example(sb);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());

    System.out.println(argCaptor.getValue().get("x").a);
  }
}

你也可以只使用 argumentCaptor<Map<String,String>>(),无需使用 @SuppressWarnings("unchecked") 或者 @Captor(junit5)。 - JustAnotherDev
@JustAnotherDev:这导致了一个异常,即变量argCaptor未初始化/在调用argCaptor.capture()时出现了NullPointerException。或者你是指其他什么? - Martin del Necesario
@MartindelNecesario,如果需要帮助,能否展示一小段代码以便我们看到更多上下文。似乎你在函数调用之前没有初始化argCaptor。 - JustAnotherDev
我发现了我的错误。我没有注意到 @Captor 注释。那么,它将通过 Mockito 进行初始化,对吗? - Martin del Necesario
是的,它应该像那样工作。 - JustAnotherDev

4
您可以使用Mockito的注释来声明的参数化实例。
例如,以下测试编译并输出了aa:
@Captor
private ArgumentCaptor<Map<String, CompoundClass>> argCaptor;

@Test
void TestDoSomehing(){
    MockitoAnnotations.initMocks(this);

    SubClass sb = mock(SubClass.class);

    Example ex = new Example(sb);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());

    System.out.println(argCaptor.getValue().get("x").a);
}

Javadocs中可以得知:

使用@Captor注释的优点之一是可以避免与捕获复杂通用类型相关的警告。


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