【问题标题】:Mockito: How to test a class's void method?Mockito:如何测试一个类的 void 方法?
【发布时间】:2020-07-01 03:08:37
【问题描述】:

单元测试新手在这里。

我有三个类:Db1DaoDb2DaoExecuteClass 其中Db1DaoDb2Dao 是两个不同数据库的数据库访问对象。我的目标是使用 Db1Dao 从 db1 获取一些数据并运行 executeClass.execute() 以使用 Db2Dao 将处理后的数据“放入”db2。

我的ExecuteClass 看起来像这样:

class ExecuteClass {

  private Db1Dao db1Dao;
  private Db2Dao db2Dao;

  public void execute() {

    ...
 
    List<String> listOfString = getExternalData(someParam);
    List<Metadata> metadatum = db1Dao.get(someInputs);
    
    ... I do something to generate a list of new class `A` based on listOfString & metadatum ...

    
    try {
        db2Dao.put(listOfA);
    } catch (PutException e){
        ...
    }
    


  }

  public List<String> getExternalData(SomeClass someParam){
    
    ... do something 
    
    return listOfString;
  }

}

现在我要测试:

给定一个特定的listOfString(由getExternalData 返回)和一个特定的metadatum(由db1Dao.get 返回):

  1. 我会得到想要的listOfA吗?

  2. 我可以调用db2Dao.put,它的输入参数是listOfA吗?

特别是,我有硬编码示例listOfStringmetadatum 和所需的listOfA(它们将通过对象MockData 传递,请参见以下代码)但我不知道如何编写使用 Mockito 进行测试。以下是我写的一个测试类,但是不起作用:

class TestClass extends BaseTest {

    @Mock
    private Db1Dao db1Dao;

    @Mock
    private Db2Dao db2Dao;

    private ExecuteClass executeClass;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        executeClass = new ExecuteClass(db1Dao, db2Dao);
    }

    @ParameterizedTest
    @MethodSource("MockDataProvider")
    public void executeClassTest(final MockData mockData) throws PutException {

        Mockito.when(db1Dao.get(Mockito.any(), ...))
                .thenReturn(mockData.getMetadatum());

        ExecuteClass executeClassSpy = Mockito.spy(executeClass);
        Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
                .thenReturn(mockData.getListOfString());

        
        executeClassSpy.execute();
        // executeClass.execute(); not working neither...

        List<A> listOfA = mockData.getDesiredListOfA();
        Mockito.verify(db2Dao).put(listOfA);
    }
}

谁能告诉我?提前谢谢你!

【问题讨论】:

    标签: mockito junit5


    【解决方案1】:

    你不应该创建你想测试的同一个类的间谍。相反,尝试为最少量的代码(例如公共方法)编写单元测试并模拟每个外部运算符(在您的情况下为 Db1DaoDb2Dao)。

    如果测试公共方法涉及调用同一类的另一个公共方法,请确保模拟另一个公共方法中的所有内容(在您的情况下为 getExternalData)。否则,这个其他公共方法可能是一个很好的候选者,可以让一个额外的类有明确的关注点分离。

    所以,删除 ExecuteClass executeClassSpy = Mockito.spy(executeClass); 并确保使用 Mockito 设置所有内容,在 getExternalData 中调用。

    实际上,验证 Db2Dao 是否使用正确的参数调用,或者使用您当前的方法验证有效负载。但在这里重要的是要 100% 创建与执行应用程序代码时相同的数据结构。

    另一个解决方案是使用 Mockito 的 @Captor。这使您可以捕获验证模拟调用的原因的价值。稍后,您还可以在捕获的值上编写断言:

    @Captor
    private ArgumentCaptor<ClassOfListOfA> argumentCaptor;
    
      
    @Test
    public void yourTest() {
    
      Mockito.verify(db2Dao).put(argumentCaptor.capture());
      assertEquals("StringValue", argumentCaptur.getValue().getWhateverGetterYouHave);
    
    }
    

    【讨论】:

    • 感谢您的评论!如果我删除“间谍”并直接使用Mockito.When(executeClass.getExternalData(Mockito.any())).thenReturn(mockData.getListOfString()),那么我得到org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 有什么建议吗?
    • 你应该模拟你正在测试的类的任何方法。尝试模拟 getExternal 方法中的任何内容
    【解决方案2】:

    以下代码对我有用。

    我部分接受了@rieckpil 的回答。我用了@Captor,非常方便。

    我不得不模拟 getExternalData() 的原因是因为它的实现仍然是一个“TODO”。

    class TestClass extends BaseTest {
    
        @Mock
        private Db1Dao db1Dao;
    
        @Mock
        private Db2Dao db2Dao;
    
        @Captor
        private ArgumentCaptor<List<A>> argumentCaptor;
    
        private ExecuteClass executeClass;
    
        @BeforeEach
        public void setUp() {
            MockitoAnnotations.initMocks(this);
            executeClass = new ExecuteClass(db1Dao, db2Dao);
        }
    
        @ParameterizedTest
        @MethodSource("MockDataProvider")
        public void executeClassTest(final MockData mockData) throws PutException {
    
            Mockito.when(db1Dao.get(Mockito.any(), ...))
                    .thenReturn(mockData.getMetadatum());
    
            ExecuteClass executeClassSpy = Mockito.spy(executeClass);
            Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
                    .thenReturn(mockData.getListOfString());
    
            executeClassSpy.execute();
    
            List<A> listOfA = mockData.getDesiredListOfA();
    
            Mockito.verify(db2Dao).put(argumentCaptor.capture());
    
            assertEquals(listOfA, argumentCaptor.getValue());
            
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-15
      • 1970-01-01
      相关资源
      最近更新 更多