web-dev-qa-db-fra.com

Comment traiter avec Setter/Getter-Methods de Mocks?

Donc, j'ai toujours des problèmes avec l'utilisation de Mockito. Supposons donc que j'ai la classe suivante (veuillez ignorer la logique ou la structure de celle-ci, ce n'est qu'un court exemple que j'ai créé à partir d'une autre classe, avec des noms différents, etc.):

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

Donc, ma compréhension des tests unitaires est d'isoler toutes les dépendances. Ici, j'aurais la classe client et le service client.

Donc pour écrire une classe de test, je ferais actuellement les choses suivantes:

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

    }
}

Donc, actuellement, je n'ai pas d'assertion, je vérifie seulement si les bonnes méthodes sont appelées. La raison pour laquelle j'essaye de faire ceci comme ceci, et de ne pas laisser la classe de test appeler le setter/getter, est à cause de l'isolement. Supposons que inputData.setCustomerCity est cassé, mon test échouerait. Cela dépend donc de la classe CustomerInputData.

Maintenant, comment puis-je aborder ces getter et setters, quelle est la meilleure pratique?

Est-ce que je n'ai pas assez bien compris Mockito? Lorsque j'utilise mock (), puis-je simplement utiliser les méthodes de définition et gethods, sans avoir à vous soucier de l'utilisation de doReturns, etc.?

Je sais que c'est un test Whitebox, je connais les méthodes et ce qui se passe.

7
user5417542

Seulement se moquer des choses que vous ne pouvez pas créer ou passer à travers vous-même. Do not mock toute entité entrée; fournir une version fausse est souvent bien supérieur.

Dans ce scénario, nous pouvons nous en tirer avec deux choses puisque nous savons quelques choses à propos de notre test:

  • Nous récupérons une instance Customer à partir de customerService, mais nous n'avons pas besoin de valider aucune sorte sur cette instance.
  • Nous avons pour simuler la variable customerService car il s’agit d’une dépendance injectée.

À la lumière de ces deux choses, nous devrions simuler CustomerService, ce que vous faites avec succès - car le champ porte le même nom, vous n'avez pas besoin des métadonnées supplémentaires dans l'annotation.

@Mock
private CustomerService customerService;

Vous devriez également envisager d'utiliser le programme d'essai fourni avec Mockito afin de ne pas avoir à initialiser explicitement les modèles.

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

Passons maintenant au test unitaire réel. La seule chose qui est vraiment nul, c'est que vous devez fournir une instance de client à utiliser, mais en dehors de cela, ce n'est pas si mal. Nos données sont l'exemple de CustomerData que nous voulons muter, et le Customer que nous fournissons pour le test. Nous devons ensuite simplement affirmer que les valeurs qui nous intéressent reviennent pour notre instance de test 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
}
7
Makoto

Ce n'est pas la "bonne" façon de s'y prendre, car se moquer des objets de valeur est largement considéré comme une mauvaise pratique (il est même dit dans la documentation de Mockito ).

Votre test devrait plutôt ressembler à ceci:

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

Pour une bonne présentation sur les tests unitaires, voir L'article récent de Martin Fowler .

2
Rogério