如何处理Mocks中的Setter/Getter-Methods?

use*_*542 7 java testing unit-testing mocking mockito

所以我仍然在使用Mockito时遇到麻烦.所以我们假设我有以下类(请忽略它的逻辑或结构,它只是我从另一个类创建的一个简短示例,具有不同的名称等等.):

public class Restaurant(
    @Autowired
    private CustomerService customerService;

    private CustomerInputData updateCustomer(CustomerInputData inputData){
        String customerId = inputData.getID();
        Customer customer = customerService.getCustomerById(customerID);
        if(customer.getAddress() != null){
            inputData.setCustomerName(customer.getCustomerName());
            inputData.setCustomerCity(customer.getCustomerCity);
            inputData.setCustomerLanguage(customer.getLanguage);
        }

        return inputData
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,我对单元测试的理解是,隔离所有依赖项.在这里,我将拥有客户级和客户服务.

所以要写一个测试类,我现在会做以下事情:

public class RestaurantTest()
{
    @Mock(name="customerService");
    private CustomerService customerService;

    @InjectMocks
    private Restaurant classUnderTest;

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

    @Test
    public void updateCustomer_WithValidInput_ShouldReturnUpdatedInput(){
        //Some Mocking first
        String customerId = "customerId";
        Customer customer = mock(Customer.class);
        CustomerInputData = mock(CustomerInputData.class);

        doReturn(customer).when(customerService.getCustomerById(any(String.class)));
        doReturn(customerId).when(inputData.getId());

        doReturn("address").when(customer.getAddress());
        doReturn("Name").when(customer.getName());
        doReturn("City").when(customer.getCity());
        doReturn("Language").when(customer.getLanguage());

        doNothing().when(inputData).setCustomerName(any(String.class));
        doNothing().when(inputData).setCustomerCity(any(String.class));
        doNothing().when(inputData).setCustomerLanguage(any(String.class));

        verify(customer.getAddress(), atLeastOnce());
        verify(customer.getName(), atLeastOnce());
        //and so on...

        verify(inputData, atLeastOnce()).setCustomerName(eq("Name"));
        verify(inputData, atLeastOnce()).setCustomerCity(eq("City"));
        verify(inputData, atLeastOnce()).setCustomerLanguage(eq("Language");

    }
}
Run Code Online (Sandbox Code Playgroud)

所以目前我没有Assert,我只检查是否调用了正确的方法.原因,为什么我尝试这样做,而不是让Test-class调用setter/getter是因为隔离.假设inputData.setCustomerCity已损坏,我的测试将失败.所以它取决于CustomerInputData-Class.

现在我如何处理这些getter和setter,最佳做法是什么?

我不明白Mockito足够好吗?当我使用mock()时,我可以只使用setter-methods和gethods,而不需要担心使用doReturns等等吗?

我知道这是一个白盒测试,我知道方法和发生了什么.

Rog*_*rio 8

这不是“正确”的方法,因为模拟值对象被广泛认为是一种不好的做法(甚至在Mockito 的文档中也这么说)。

您的测试应该如下所示:

@Test
public void updateCustomer_WithValidInput_ShouldReturnUpdatedInput() {
    String customerId = "customerId";
    String name = "Name";
    String address = "address";
    String language = "language";
    Customer customer = new Customer();
    customer.setName(name);
    customer.setAddress(address);
    customer.setLanguage(language);
    CustomerInputData inputData = new CustomerInputData();
    inputData.setId(customerId);

    doReturn(customer).when(customerService).getCustomerById(customerId);

    CustomerInputData updatedInput = classUnderTest.updateCustomer(inputData);

    assertSame(inputData, updatedInput);
    assertEquals(name, updatedInput.getCustomerName());
    assertEquals(address, updatedInput.getCustomerCity());
    assertEquals(language, updatedInput.getLanguage());
}
Run Code Online (Sandbox Code Playgroud)

有关单元测试的精彩演示,请参阅Martin Fowler 最近的文章


Mak*_*oto 7

只嘲笑你无法创造或通过自己的东西. 难道不是嘲笑任何传入的实体; 提供假冒版本通常要优越得多.

在这种情况下,我们可以解决一些问题,因为我们知道有关测试的一些事情:

  • 我们Customer从中获取实例customerService,但我们不需要对该实例进行任何类型的验证.
  • 我们必须嘲笑它customerService,因为它是一个注入的依赖.

鉴于这两件事,我们应该嘲笑CustomerService你,你做了哪些成功 - 因为字段命名相同,你不需要注释中的额外元数据.

@Mock
private CustomerService customerService;
Run Code Online (Sandbox Code Playgroud)

您还应该使用Mockito提供的测试运行器,这样您就不必显式初始化模拟.

@RunWith(MockitoJUnitRunner.class)
public class RestaurantTest {
    // tests
}
Run Code Online (Sandbox Code Playgroud)

现在,进行实际的单元测试.唯一真正糟糕的是你必须提供一个客户的实例来使用,但除此之外,它并不是太糟糕.我们的恩惠是CustomerData我们想要变异的例子,我们Customer提供测试.然后,我们必须简单地断言我们关心的值将返回给我们的测试CustomerData实例.

@Test
public void updateCustomer_WithValidInput_ShouldReturnUpdatedInput(){
    //given
    final Customer customer = new Customer();
    customer.setId("123");
    customer.setAddress("Addr1");
    customer.setName("Bob");
    customer.setCity("St8")
    customer.setLanguage("Java");

    final CustomerInputData inputData = new CustomerInputData();
    inputData.setId(customer.getId());

    //when
    when(customerService.getCustomerById(customer.getId())).thenReturn(customer);
    classUnderTest.updateCustomer(customerData);

    //then
    verify(customerService.getCustomerById("123"));
    assertThat(customerData.getCustomerName(), equalTo(customer.getName()))
    // and so forth
}
Run Code Online (Sandbox Code Playgroud)