GLO-4002 - Site du cours 2023

Temps estimé: 2 heures

Écriture de tests non unitaires

Maintenant que vous êtes rendu pro à écrire des tests unitaires, il est temps de pratiquer les tests non-unitaires!

Préparation

Pour sauvez du temps, nous avons créés une application pour vous: Non-Unit Tests

L'application est simple, elle fait deux choses:

    1. Ajoute un ami.
    1. Retourne la liste de tous les amis ajoutés (je sais, c'est irréaliste d'avoir plusieurs amis, mais ce n'est qu'un exemple).

Question 1 - Tests de bordure (Service + Domaine)

Après avoir cloné le projet, modifiez la classe de test FriendServiceIT en écrivant les tests de bordure nécessaires pour assurer le comportement entre la couche service et la couche du domaine.

public class FriendServiceIT {

    private static final String A_NAME = "This is a valid name";
    private static final String INVALID_NAME = null;

    private FriendRepository friendRepositoryMock;

    private FriendService friendService;

    @BeforeEach
    public void setup() {
        friendRepositoryMock = mock(FriendRepository.class);
        friendService = new FriendService(new NameValidator(), new FriendFactory(), friendRepositoryMock, new FriendAssembler());
    }

    @Test
    public void givenInvalidName_whenMakingFriend_thenNothingIsSaved() {
        assertThrows(InvalidNameException.class, () -> friendService.makeFriend(INVALID_NAME));
        verify(friendRepositoryMock, never()).save(any());
    }

    @Test
    public void givenAName_whenMakingFriend_thenFriendIsSavedWithThatNameUpperCased() {
        friendService.makeFriend(A_NAME);
        verify(friendRepositoryMock).save(argThat(friend -> friend.getName().equals(A_NAME.toUpperCase())));
    }

    @Test
    public void whenFindingAllFriends_thenAllFriendsAreReturnedWithLowerCasedNames() {
        String name1 = "BOB ROSS";
        String name2 = "CHUCK NORRIS";
        String name3 = "DONALD DUCK";
        List<String> expectedLowerCasedNames = Arrays.asList(name1.toLowerCase(), name2.toLowerCase(), name3.toLowerCase());
        when(friendRepositoryMock.findAllFriends()).thenReturn(Arrays.asList(new Friend(name1), new Friend(name2), new Friend(name3)));

        List<FriendDto> friends = friendService.findAllFriends();
        List<String> friendNames = friends.stream().map(FriendDto::getName).collect(Collectors.toList());

        assertEquals(expectedLowerCasedNames, friendNames);
    }
}

L'important ici est de comprendre qu'on ne cherche pas à revérifier ce qui est déjà couvert par les tests unitaires.

On veut s'assurer que le comportement du domaine est respecté et remonté jusque dans la couche service (ex. uppercase).

En plus de valider que les différents services font ce qu'ils ont à faire avec le domaine.

Note: Le repository de la couche Infra est mocké dans ce type de tests.

Question 2 - Tests de bordure (Persistence en-mémoire)

Modifiez la classe de test FriendRepositoryIT avec les tests nécessaires pour assurer le bon fonctionnement de la persistence des données.

public class FriendRepositoryIT {

    private static final Friend FRIEND1 = new Friend("Alan");
    private static final Friend FRIEND2 = new Friend("Britney");
    private static final Friend FRIEND3 = new Friend("Zyra");
    private static final List<Friend> FRIENDS = Arrays.asList(FRIEND1, FRIEND2, FRIEND3);
    private static final List<FriendDto> EXPECTED_FRIENDS = mock(List.class);

    private FriendRepository friendRepository;

    @BeforeEach
    public void setup() {
        friendRepository = new InMemoryFriendRepository();
    }

    @Test
    public void givenFriends_whenFindingAllFriends_thenThoseFriendsAreFound() {
        given(FRIENDS);
        List<Friend> actualFriends = friendRepository.findAllFriends();
        assertEquals(FRIENDS, actualFriends);
    }

    private void given(List<Friend> friends) {
        friends.forEach(friend -> friendRepository.save(friend));
    }
}

Question 3 - Tests de bordure (Persistence avec base de données)

Comment feriez-vous pour tester non-unitairement le bon fonctionnement de la persistence vers une base de données?

Il est important de s'assurer de ne pas tester avec la vrai base de données. Sinon, les données utilisées par d'autres personnes pourraient être corrompues.

Certaines solutions impliques l'utilisation de bases de données intégrées (ex. h2database) ou d'un conteneur de tests, afin d'assurer un comportement stable et contrôlé dans les tests.

Question 4 - Tests d'API

Modifiez la classe de test FriendResourceIT de sorte qu'elle s'assure du bon fonctionnement du point d'entrée de l'application.

Pour ces tests, il faut s'assurer de mocker la couche de service vu que c'est le comportement de l'API qui nous intéresse.

Les comportements à vérifier:

  • Le 'MediaType' est 'JSON'. (*)
  • Les différents 'Path' accessibles. (*)
  • Les différents types de 'StatusType' possibles en réponse (ex. 200, 400).
  • Les restrictions en lien avec la sérialization avec 'Jackson' de la réponse (ex. Include.NON_NULL).

(*) Certains comportements peuvent être testés indirectement à l'intérieur d'un autre test.