简体   繁体   English

验证模拟对象内的对象是否传递到Mockito中的参数中

[英]Verifying objects within a mocked object are passed into argument in Mockito

Question Context 问题背景

I have a wrapper class with two objects that contain a list. 我有一个包装器类,其中包含两个包含列表的对象。 (ie Class1 and Class2 both have a Widget List.) (即Class1和Class2都有一个小部件列表。)

public class WrapperClass {
    Class1 class1;
    Class2 class2;
}

I have a utils class which processes the Wrapper class 我有一个utils类,它处理Wrapper类

public class WrapperUtils {

    public void processClasses() {
        WrapperClass wrapperClass = getWrapperClass();
        doSomething(class1.getWidgetList());
        doSomething(class2.getWidgetList());
    }

    private WrapperClass getWrapperClass() {
        return wrapperClassFromOnlineService;
    }

    public void doSomething(List<Widget> widgetList) {}
}

Goal 目标

I'd like to use Mockito to verify that the doSomething method gets called with class1's widget list. 我想使用Mockito来验证doSomething方法是否被class1的小部件列表调用。

My Attempt 我的尝试

@Test
public void main(String[] args) {
    WrapperClass wrapperClass = new WrapperClass();
    wrapperClass.class1 = new Class1();
    wrapperClass.class2 = new Class2();

    WrapperUtils utils = new WrapperUtils();
    Mockito.when(utils.getWrapperClass()).thenReturn(wrapperClass);

    Mockito.verify(utils, times(1)).doSomething(wrapperClass.class1.getWidgetList());
    Mockito.verify(utils, times(1)).doSomething(wrapperClass.class2.getWidgetList());
}

Results of above code: doSomething registers as being called twice for both verify statements. 上面代码的结果:doSomething注册为两个verify语句两次被调用。 My guess is that the widget lists are treated as the same? 我的猜测是小部件列表被视为相同?

This code has a design issue that makes it difficult to test with mocks. 该代码具有一个设计问题,很难通过模拟进行测试。

  1. The test does not expose clearly which object is being tested, and which interaction. 该测试不能清楚地暴露出正在测试哪个对象以及哪个交互。
  2. This test is testing the implementation not behavior, that should be the opposite. 此测试是测试实现而不是行为,应该相反。
  3. Because of problem #1, mockito is misused 由于问题1,mockito被滥用

What that mean is that 那是什么意思

  1. The test should show the case/scenario being tested, clear separations of the fixture, the invocation being tested, and the assertion/verification. 测试应显示要测试的案例/场景,固定装置的清晰分隔,要测试的调用以及断言/验证。
  2. The test should test behavior, that is interactions between the tested objects and collaborators, not internal (as it may change without breaking the test). 测试应该测试行为,即被测试对象与协作者之间的交互 ,而不是内部的(因为它可能会在不破坏测试的情况下发生变化)。 Also the test can test an expected output given an input. 测试还可以测试给定输入的预期输出。
  3. If #1 and #2 are addressed then it is obvious which type has to be mocked, and to follow tutorials here and there. 如果解决了#1和#2的问题,那么显然必须模拟哪种类型,并在此处和那里按照教程进行操作。

Here's an idea of how I'd write the test, this code is mainly focused on interactions, but it is possible to focus the assertions on the state of Class (don't mock them in this case !!!) : 这是我如何编写测试的一个想法,该代码主要集中于交互,但是可以将断言集中在Class的状态上(在这种情况下,请不要嘲笑它们!):

@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
    @Mock Class class1;
    @Mock Class class2;

    @Test public void ensure_that____whatever() {
        // given
        WrapperUtils tested_utils = new WrapperUtils(new WrapperClass(class1, class2));

        // when
        tested_utils.processClass();

        // then
        verify(class1).someInteraction();
        verify(class2).someInteraction();
    }
}

And the implementation could look like 实现看起来像

public class WrapperUtils {
    private WrapperClass wrapperClass;
    public WrapperUtils(WrapperClass wrapperClass) {
        this.wrapperClass = wrapperClass;
    }

    public void processClasses() {
        doSomething(wrapperClass.class1);
        doSomething(wrapperClass.class2);
    }

    public void doSomething(Class clazz) {
        clazz.someInteraction();
    }
}

Note the wrapperClass is injected in the WrapperUtils via the constructor, that works but you can also pass a Supplier (available in guava or in JDK8), this supplier could get the data from anywhere, a webservice for exemple. 请注意, wrapperClass是通过构造函数注入WrapperUtils ,该函数可以工作,但您也可以传递Supplier (在guava或JDK8中可用),此Supplier可以从任何地方获取数据,例如Web服务。 Or it could your type. 也可以是您的类型。 In the test the supplier would be a mock. 在测试中,供应商将是一个模拟。

@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
    @Mock Class class1;
    @Mock Class class2;
    @Mock Supplier<WrapperClass> wrapped_class_supplier;

    @Test public void ensure_that____whatever() {
        // given
        BDDMockito.given(wrapped_class_supplier.get()).willReturn(new WrapperClass(class1, class2));
        WrapperUtils tested_utils = new WrapperUtils(wrapped_class_supplier);

        // when
        tested_utils.processClass();

        // then
        verify(class1).someInteraction();
        verify(class2).someInteraction();
    }
}

I strongly advice you to follow Test Driven Development methodology it really helps to write good software. 我强烈建议您遵循“ 测试驱动开发”方法,这确实有助于编写出色的软件。 Also there's this book Growing Object Oriented Software Guided by Tests that is a great read, the book may seem old but it is still describes best practices. 此外,还有一本书, 《由测试指导的面向对象的成长软件 》( Growing Object Oriented Software by Tests)读得很好,这本书看似古老,但仍在描述最佳实践。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM