wake-up-neo.net

Wie gehe ich mit Setter/Getter-Methoden aus Mocks um?

Daher habe ich immer noch Probleme mit der Verwendung von Mockito. Nehmen wir also an, ich habe folgende Klasse (Bitte ignorieren Sie die Logik oder Struktur, es ist nur ein kurzes Beispiel, das ich aus einer anderen Klasse mit anderen Namen usw. erstellt habe.):

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

Mein Verständnis von Unit-Tests ist es also, alle Abhängigkeiten zu isolieren. Hier hätte ich die Kundenklasse und den Kundenservice.

Um eine Testklasse zu schreiben, würde ich momentan folgendes machen:

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

    }
}

Daher habe ich derzeit keine Zusicherung, ich überprüfe nur, ob die richtigen Methoden aufgerufen werden. Der Grund, warum ich dies so versuche und die Test-Klasse nicht den Setter/Getter aufrufen lasse, liegt in der Isolation. Nehmen wir an, inputData.setCustomerCity ist defekt, mein Test würde fehlschlagen. Es ist also abhängig von der CustomerInputData-Klasse.

Wie gehe ich nun mit diesen Gettern und Setzern um? Was ist die beste Vorgehensweise?

Verstehe ich Mockito nicht gut genug? Wenn ich mock () verwende, kann ich dann einfach die setter-Methoden und gethods verwenden und muss mich nicht um die Verwendung von doReturns und so weiter kümmern?

Ich weiß, es ist ein Whitebox-Test, ich kenne die Methoden und was los ist.

7
user5417542

Verspotten Sie nur die Dinge, die Sie nicht selbst schaffen können. Do nicht keine übergebenen Entitäten; eine gefälschte Version anzubieten, ist oft weit überlegen.

In diesem Szenario können wir mit ein paar Dingen davonkommen, da wir einige Dinge über unseren Test wissen:

  • Wir erhalten eine Customer-Instanz von customerService zurück, aber wir müssen für diese Instanz keinerlei Validierung durchführen.
  • Wir haben um customerService zu verspotten, da es sich um eine injizierte Abhängigkeit handelt.

In Anbetracht dieser beiden Dinge sollten wir CustomerService ausspionieren, was Sie irgendwie erfolgreich tun - da das Feld denselben Namen hat, benötigen Sie keine zusätzlichen Metadaten in der Anmerkung.

@Mock
private CustomerService customerService;

Sie sollten auch versuchen, den mit Mockito gelieferten Testrunner zu verwenden, damit Sie die Mocks nicht explizit initialisieren müssen.

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

Nun zum eigentlichen Gerätetest. Das einzige, was wirklich scheiße ist, dass Sie eine Instanz eines Kunden zur Verfügung stellen müssen, aber außerhalb davon ist es nicht so schlimm. Unsere Gaben sind die Instanz von CustomerData, die wir mutieren möchten, und die Customer, die wir für den Test bereitstellen. Wir müssen dann einfach die Werte, die uns wichtig sind, für unsere Test- CustomerData-Instanz bestätigen.

@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

Dies ist nicht der "richtige" Weg, um darüber nachzugehen, da das Spotting von Wertobjekten weithin als schlechte Praxis angesehen wird (es heißt sogar in Mockitos Dokumentation ).

Ihr Test sollte stattdessen so aussehen:

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

Eine gute Präsentation zum Testen von Bauteilen finden Sie in Martin Fowlers neuem Artikel .

2
Rogério