GLO-4002 - Site du cours 2023

Temps estimé: 60 minutes

Exercices : Doubler à bras

Maintenant que vous savez ce que c'est qu'un mock, nous allons en créer un manuellement ensemble!

Voici les étapes à suivre:

Préparation

1. Créer un nouveau projet

Ouvrez votre IDE de préférence et créez un nouveau projet Java Maven avec JDK 8 ou une version plus récente. Assurez-vous également d'importer une version récente de JUnit dans la liste des dépendances Maven.

pom.xml:
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.6.2</version>
    <scope>test</scope>
</dependency>

2. Ajouter le code

Pour l'exercice, nous avons un téléphone qui permet le "swipe" vers la droite sur une application de rencontre en ligne. Pour vous aider, on vous donne le code suivant:

DatingApp.java:
public class DatingApp {

    public void swipeRight() {
        System.out.print("Hello there.");
    }
}
Phone.java:
public class Phone {

    private final DatingApp datingApp;

    public void swipeRight() {
        datingApp.swipeRight();
    }
}
DatingAppTest.java:
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class DatingAppTest {

    private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    @BeforeEach
    public void setup() {
        System.setOut(new PrintStream(outputStream));
    }

    @Test
    public void whenSwipingRight_thenSayHello() {
        DatingApp datingApp = new DatingApp();

        datingApp.swipeRight();

        assertEquals("Hello there.", outputStream.toString());
    }
}

Exercice

1. Tester le comportement du téléphone

Pour vous aider à bien comprendre le fonctionnement d'un mock, nous allons en fabriquer un "à bras", c'est-à-dire sans utiliser de librairies externes. Pour ce faire, créez la classe de test PhoneTest.java avec le ou les tests nécessaires tout en respectant les critères d'acceptation suivants:

  • S'assurer que tous les comportements du téléphone soient testés.
  • Modifier seulement les classes PhoneTest.java et Phone.java.
  • Importer aucune librairie externe autre que JUnit.

Conseil: Vous allez vite vous rendre compte qu'il est nécessaire d'injecter les dépendances dans Phone.java.

Phone.java:

public class Phone {

   private final DatingApp datingApp;

    // L'ajout du mock nécessite l'injection du `DatingApp` dans le constructeur de la clase `Phone`.
    public Phone(DatingApp datingApp) {
        this.datingApp = datingApp;
    }

    public void swipeRight() {
        datingApp.swipeRight();
    }
}

PhoneTest.java:

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class PhoneTest {

   @Test
    public void whenSwipingRight_thenDatingAppIsSwipedRight() {
        DatingAppMock datingApp = new DatingAppMock();
        Phone phone = new Phone(datingApp);

        phone.swipeRight();

        assertTrue(datingApp.swipeRightCalled);
    }

    private class DatingAppMock extends DatingApp {

        public boolean swipeRightCalled = false;

        @Override
        public void swipeRight() {
            this.swipeRightCalled = true;
        }
    }
}

2. Ajouter la méthode "canSwipeLeft"

Faire maintenant le même exercice, mais avec la méthode swipeLeft qui doit être ajoutée dans la classe Phone.

Phone.java:

public String swipeLeft() throws Exception {
    if (!datingApp.canSwipeLeft()) {
        throw new Exception("User no swiping!");
    }

    return "General Kenobi";
}

Respectez les critères d'acceptation suivants:

  • La méthode canSwipeLeft retourne un boolean: true si le "swipe" à droite a été effectué au moins une fois, false sinon (on peut aller à gauche uniquement si on a été à droite avant).
  • Tous les nouveaux comportements ajoutés doivent être testés.
  • Ne pas modifier le code de la classe Phone.

DatingApp.java:

public class DatingApp {

    private boolean hasSwipedRight = false;

    public void swipeRight() {
        hasSwipedRight = true;
    }

    public boolean canSwipeLeft() {
        return this.hasSwipedRight;
    }
}

PhoneTest.java:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class PhoneTest {

    private DatingAppMock datingAppMock;
    private Phone phone;

    @BeforeEach
    public void setup() {
        datingAppMock = new DatingAppMock();
        phone = new Phone(datingApp);
    }

    @Test
    public void whenSwipingRight_thenDatingAppIsSwipedRight() {
        phone.swipeRight();

        assertTrue(datingAppMock.swipeRightCalled);
    }

    @Test
    public void givenADatingAppThatCannotSwipeLeft_whenSwipingLeft_thenPreventSwipingLeft() {
        datingAppMock.givenCannotSwipeLeft();

        assertThrows(Exception.class, () -> phone.swipeLeft());
    }

    @Test
    public void givenADatingAppThatCanSwipeLeft_whenSwipingLeft_thenSayGeneralKenobi() throws Exception {
        datingAppMock.givenCanSwipeLeft();

        String answer = phone.swipeLeft();

        String expectedAnwser = "General Kenobi"; // Constantes? (voir plus tard dans le cours)
        assertEquals(expectedAnwser, answer);
    }

    private class DatingAppMock extends DatingApp {

        private boolean canSwipeLeft = false;
        public boolean swipeRightCalled = false;

        public void givenCanSwipeLeft() {
            this.canSwipeLeft = true;
        }

        public void givenCannotSwipeLeft() {
            this.canSwipeLeft = false;
        }

        @Override
        public void swipeRight() {
            this.swipeRightCalled = true;
        }

        @Override
        public boolean canSwipeLeft() {
            return canSwipeLeft;
        }
    }
}

DatingAppTest.java:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class DatingAppTest {

    private DatingApp datingApp;

    @BeforeEach
    public void setup() {
        datingApp = new DatingApp();
    }

    @Test
    public void givenNeverSwipedRight_whenCheckingIfSwipingLeftIsPossible_thenSwipingLeftIsNotPossible() {
        assertFalse(datingApp.canSwipeLeft());
    }

    @Test
    public void givenAlreadySwipedRight_whenCheckingIfSwipingLeftIsPossible_thenSwipingLeftIsPossible() {
        datingApp.swipeRight();

        assertTrue(datingApp.canSwipeLeft());
    }
}

Notez que la réponse obtenu d'un mock peut être très importante lorsqu'elle impacte la classe testée, comme c'est le cas avec Phone et la méthode mockée canSwipeLeft.