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

时间:2015-10-29 10:08:37

标签: java unit-testing 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
    }
}

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

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

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");

    }
}

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

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

我对Mockito的理解还不够好吗?当我使用mock()时,我是否可以使用setter-methods和gethods,而不必担心使用doReturns等等?

我知道这是一个白盒测试,我知道方法和发生的事情。

2 个答案:

答案 0 :(得分:7)

Only mock the things which you cannot create or pass through yourself. Do not mock any passed-in entities; providing a fake version is often far superior.

In this scenario, we can get away with a couple of things since we know a few things about our test:

  • We are getting back a Customer instance from the customerService, but we don't need to do any sort of validation on that instance.
  • We have to mock the customerService out since it is an injected dependency.

In light of these two things, we should mock out CustomerService, which you do kind of successfully - since the field is named the same you don't need the extra metadata in the annotation.

@Mock
private CustomerService customerService;

You should also look to use the test runner provided with Mockito so you don't have to explicitly initialize the mocks.

@RunWith(MockitoJUnitRunner.class)
public class RestaurantTest {
    // tests
}

Now, on to the actual unit test. The only thing that really sucks is that you have to provide an instance of a Customer to use, but outside of that, it's not too bad. Our givens are the instance of CustomerData we want to mutate, and the Customer we're providing for test. We then have to simply assert the values that we care about are coming back for our test CustomerData instance.

@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
}

答案 1 :(得分:2)

这不是“正确”的方式,因为嘲弄价值对象被广泛认为是一种不好的做法(它甚至在Mockito's documentation中也这样说。)

您的测试应该是这样的:

@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());
}

有关单元测试的详细介绍,请参阅Martin Fowler's recent article