【问题标题】:confused by unit testing and Mockito被单元测试和 Mockito 弄糊涂了
【发布时间】:2014-12-28 14:09:22
【问题描述】:

我是单元测试和 Mockito 的新手。我对应该测试和验证的内容感到困惑。我有一个 A 类如下:

public class A{
    @Autowired
    private B b;

    public double doSomething(Request r){
        r = b.process1(r);
        r = b.process2(r);
        return calculateFinal(r);
    }

    public void reportSomething(Request r){
        r = b.process1(r);
        r = b.process2(r);
        b.report(r);
    }

    private int calculateFinal(Request r){
        return r.getFinalScore() * 2;
    }
}

假设我想用 Junit 测试来测试这两种方法。因为我在 A 中有一个依赖项 B,所以我用 Mockito 模拟它。对于这两个测试,我都被告知我应该假设依赖项 b 已经过全面测试并且可以正常工作,因为我们要测试 A 中的业务逻辑。

起初看起来我不需要为reportSomething() 测试任何东西,因为它只涉及对b 的调用并且它们都在“工作”?我能想到的唯一要测试的是它们是否真的被调用以及调用的顺序,对吗?那么我应该调用 a.reportSomething() 然后进行验证吗?让我烦恼的一件事是我是否应该存根 b.process1() 和 b.process2() 以返回任何内容。我试过没有存根任何东西,但它起作用了,但为什么呢?

对于testDoSomething(),我想我真正测试的是calculateFinal() 方法。但由于它使用来自 Request 对象的数据,我需要先在 Request r 中设置该数据。由于r 直接来自b.process2(),我应该存根方法调用以返回带有该数据的请求对象。但我可以跳过b.process1() 的存根,对吧?

这是一个正确的思考过程吗?我错过了什么或误解了什么?如果是对的,有没有更好更干净的写法呢?谢谢!

public class ATest{
    private static final int SCORE = 100;

    @Mock
    private B mockB;

    @InjectMocks
    private A aClient;

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

    @Test
    public void testReportSomething(){
        // what should I test here?

        Request r = new Request();

        // is it necessary to include the following two lines?
        when(mockB.process1(any(Request.class))).return(r);
        when(mockB.process2(any(Request.class))).return(r);


        aClient.reportSomething(r);
        InOrder inOrder = inOrder(mockProcesser);
        inOrder.verify(mockProcesser).process1(any(Request.class));
        inOrder.verify(mockProcesser).process2(any(Request.class));
        inOrder.verify(mockProcesser).report(any(Request.class));
    }

    @Test
    public void testDoSomething(){
        // Is this correct?
        Request r = new Request();
        r.setFinal(SCORE);

        // I skipped this line and it still works
        when(mockB.process1(any(Request.class))).return(r);

        when(mockB.process2(any(Request.class))).return(r);
        assert(SCORE * 2, aClient.doSomething(r));
        // is it still necessary to verify the call to mockB?
    }
}

【问题讨论】:

    标签: java unit-testing junit mocking mockito


    【解决方案1】:

    您的测试不正确。我们来看看你要测试的方法:

    public void reportSomething(Request r){
        r = b.process1(r);
        r = b.process2(r);
        b.report(r);
    }
    

    首先,你需要mock一下,当b处理一个请求时,它会返回预期的结果;因此,请勿对两次调用使用相同的返回值。

    这是我将如何编写测试:

    final Request r = mock(Request.class);
    final Request r1 = mock(Request.class);
    final Request r2 = mock(Request.class);
    
    when(mockB.process1(r)).thenReturn(r1);
    when(mockB.process2(r1)).thenReturn(r2);
    doNothing().when(mockB).report(any(Request.class));
    
    final InOrder inOrder = inOrder(mockB);
    
    // Launch... And then verify:
    
    inOrder.verify(mockB).process1(r);
    inOrder.verify(mockB).process2(r1);
    inOrder.verify(mockB).report(r2);
    inOrder.verifyNoMoreInteractions();
    

    至于:

      // is it necessary to include the following two lines?
    

    是的。默认情况下,当未指定时,模拟实例将返回 Java 的默认值:0 表示数字基元,false 表示布尔值,null 表示对象。您必须指定要通过存根返回的内容。

    【讨论】:

    • 那么即使我们知道依赖b正常工作,我们仍然需要通过“当b处理请求时,它返回预期结果”来模拟它?另外,为什么doNothing().when(mockB).report(any(Request.class)); 而不是doNothing().when(mockB).report(r2);?谢谢。
    • b 可能工作正常,但在这里你不测试b;与第二点一样,它是拓宽存根的范围。重要的是验证,您想测试 b.report() 是否被 r2 调用。
    • 在旁注中,我要补充一点,代码完成后的测试通常要复杂得多,因为代码对测试的友好性要低得多。如果需要模拟,则无需重构,这将变得更加微妙。
    猜你喜欢
    • 1970-01-01
    • 2022-01-23
    • 2019-01-12
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 2014-08-14
    • 2016-02-16
    • 1970-01-01
    相关资源
    最近更新 更多