Recherche…


Simple Mock

Mockito propose un code unique pour créer des simulations de classes et d’interfaces (non finales).

Dependency mock = Mockito.mock(Dependency.class);

Cela crée une instance fictive de Dependency indépendamment du fait que la Dependency soit une interface ou une classe.

Il est alors possible de modifier les appels de méthode à cette maquette en utilisant la notation Mockito.when (x) .thenReturn (y).

Mockito.when(mock.possiblyBuggyMethod()).thenReturn("someString");

Donc, les appels à Dependency.possiblyBuggyMethod() renvoient simplement "someString" .

Il y a une autre notation qui est déconseillée dans la plupart des cas d'utilisation car elle n'est pas typée.

Mockito.doReturn("someString").when(mock).possiblyBuggyMethod()

Se moquer des défauts

Alors qu'une simple maquette renvoie null (ou les valeurs par défaut pour les primitives) à chaque appel, il est possible de modifier ce comportement.

Dependency mock = Mockito.mock(Dependency.class, new Answer() {

        @Override
        public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
            return "someString";
        }
    });

ou en utilisant des lambdas:

Dependency mock = Mockito.mock(Dependency.class, (Answer) invocationOnMock -> "someString");

Cet exemple renvoie "someString" à chaque invocation mais il est possible de définir n'importe quelle logique dans la méthode de réponse.

Se moquer d'une classe à l'aide d'annotations

Classe sous test:

public class GreetingsService { // class to be tested in isolation
    private UserService userService;

    public GreetingsService(UserService userService) {
        this.userService = userService;
    }

    public String getGreetings(int userId, LocalTime time) { // the method under test
        StringBuilder greetings = new StringBuilder();
        String timeOfDay = getTimeOfDay(time.getHour());
        greetings.append("Good ").append(timeOfDay).append(", ");
        greetings.append(userService.getFirstName(userId)) // this call will be mocked
                .append(" ")
                .append(userService.getLastName(userId)) // this call will be mocked
                .append("!");
        return greetings.toString();
    }

    private String getTimeOfDay(int hour) { // private method doesn't need to be unit tested
        if (hour >= 0 && hour < 12)
            return "Morning";
        else if (hour >= 12 && hour < 16)
            return "Afternoon";
        else if (hour >= 16 && hour < 21)
            return "Evening";
        else if (hour >= 21 && hour < 24)
            return "Night";
        else
            return null;
    }
}

Le comportement de cette interface sera moqué:

public interface UserService {
    String getFirstName(int userId);

    String getLastName(int userId);
}

Supposons une implémentation réelle du UserService :

public class UserServiceImpl implements UserService {
    @Override
    public String getFirstName(int userId) {
        String firstName = "";
        // some logic to get user's first name goes here
        // this could be anything like a call to another service,
        // a database query, or a web service call
        return firstName;
    }

    @Override
    public String getLastName(int userId) {
        String lastName = "";
        // some logic to get user's last name goes here
        // this could be anything like a call to another service,
        // a database query, or a web service call
        return lastName;
    }
}

Test Junit avec Mockito:

public class GreetingsServiceTest {
    @Mock
    private UserServiceImpl userService; // this class will be mocked
    @InjectMocks
    private GreetingsService greetingsService = new GreetingsService(userService);

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

    @Test
    public void testGetGreetings_morning() throws Exception {
        // specify mocked behavior
        when(userService.getFirstName(99)).thenReturn("John");
        when(userService.getLastName(99)).thenReturn("Doe");
        // invoke method under test
        String greetings = greetingsService.getGreetings(99, LocalTime.of(0, 45));
        Assert.assertEquals("Failed to get greetings!", "Good Morning, John Doe!", greetings);
    }

    @Test
    public void testGetGreetings_afternoon() throws Exception {
        // specify mocked behavior
        when(userService.getFirstName(11)).thenReturn("Jane");
        when(userService.getLastName(11)).thenReturn("Doe");
        // invoke method under test
        String greetings = greetingsService.getGreetings(11, LocalTime.of(13, 15));
        Assert.assertEquals("Failed to get greetings!", "Good Afternoon, Jane Doe!", greetings);
    }
}

"Spy" pour se moquer partiellement

L'annotation @Spy (ou la méthode ) peut être utilisée pour simuler partiellement un objet. Ceci est utile lorsque vous souhaitez simuler partiellement le comportement d'une classe. Par exemple, supposons que vous ayez une classe qui utilise deux services différents et que vous souhaitez simuler une seule d'entre elles et utiliser l'implémentation réelle de l'autre service.

Note: Bien que je pense philosophiquement que ce ne soit pas un "test unitaire pur", car vous intégrez une classe réelle et ne testez pas votre classe sous test de manière totalement isolée. Néanmoins, cela pourrait être utile dans le monde réel et je l'utilise souvent lorsque je me moque de la base de données en utilisant une implémentation de base de données en mémoire afin que je puisse utiliser de vrais DAO.

Class under test:
public class GreetingsService { // class to be tested in isolation
    private UserService userService;
    private AppService appService;

    public GreetingsService(UserService userService, AppService appService) {
        this.userService = userService;
        this.appService = appService;
    }

    public String getGreetings(int userId, LocalTime time) { // the method under test
        StringBuilder greetings = new StringBuilder();
        String timeOfDay = getTimeOfDay(time.getHour());
        greetings.append("Good ").append(timeOfDay).append(", ");
        greetings.append(userService.getFirstName(userId)) // this call will be mocked
                .append(" ")
                .append(userService.getLastName(userId)) // this call will be mocked
                .append("!");
        greetings.append(" Welcome to ")
                .append(appService.getAppName()) // actual method call will be made
                .append(".");
        return greetings.toString();
    }

    private String getTimeOfDay(int hour) { // private method doesn't need to be unit tested
        if (hour >= 0 && hour < 12)
            return "Morning";
        else if (hour >= 12 && hour < 16)
            return "Afternoon";
        else if (hour >= 16 && hour < 21)
            return "Evening";
        else if (hour >= 21 && hour < 24)
            return "Night";
        else
            return null;
    }
}

Le comportement de cette interface sera moqué:

public interface UserService {
    String getFirstName(int userId);

    String getLastName(int userId);
}

Supposons une implémentation réelle du UserService :

public class UserServiceImpl implements UserService {
    @Override
    public String getFirstName(int userId) {
        String firstName = "";
        // some logic to get user's first name
        // this could be anything like a call to another service,
        // a database query, or a web service call
        return firstName;
    }

    @Override
    public String getLastName(int userId) {
        String lastName = "";
        // some logic to get user's last name
        // this could be anything like a call to another service,
        // a database query, or a web service call
        return lastName;
    }
}

Le comportement de cette interface ne sera pas moqué:

public interface AppService {
    String getAppName();
}

Supposons une implémentation réelle d' AppService :

public class AppServiceImpl implements AppService {
    @Override
    public String getAppName() {
        // assume you are reading this from properties file
        String appName = "The Amazing Application";
        return appName;
    }
}

Test Junit avec Mockito:

public class GreetingsServiceTest {
    @Mock
    private UserServiceImpl userService; // this class will be mocked
    @Spy
    private AppServiceImpl appService; // this class WON'T be mocked
    @InjectMocks
    private GreetingsService greetingsService = new GreetingsService(userService, appService);

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

    @Test
    public void testGetGreetings_morning() throws Exception {
        // specify mocked behavior
        when(userService.getFirstName(99)).thenReturn("John");
        when(userService.getLastName(99)).thenReturn("Doe");
        // invoke method under test
        String greetings = greetingsService.getGreetings(99, LocalTime.of(0, 45));
        Assert.assertEquals("Failed to get greetings!", "Good Morning, John Doe! Welcome to The Amazing Application.", greetings);
    }

    @Test
    public void testGetGreetings_afternoon() throws Exception {
        // specify mocked behavior
        when(userService.getFirstName(11)).thenReturn("Jane");
        when(userService.getLastName(11)).thenReturn("Doe");
        // invoke method under test
        String greetings = greetingsService.getGreetings(11, LocalTime.of(13, 15));
        Assert.assertEquals("Failed to get greetings!", "Good Afternoon, Jane Doe! Welcome to The Amazing Application.", greetings);
    }
}

Définir des champs privés dans des objets simulés

Dans votre classe en cours de test, vous pouvez avoir des champs privés qui ne sont pas accessibles, même via un constructeur. Dans de tels cas, vous pouvez utiliser la réflexion pour définir ces propriétés. Ceci est un extrait de ce test JUnit.

@InjectMocks
private GreetingsService greetingsService = new GreetingsService(); // mocking this class

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    String someName = "Some Name";
    ReflectionTestUtils.setField(greetingsService, // inject into this object
        "name", // assign to this field
        someName); // object to be injected
}

J'utilise la méthode ReflectionTestUtils.setField(Object targetObject, String name, Object value) pour simplifier, mais vous pouvez utiliser la méthode Java Reflection pour faire la même chose.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow