User Tools

Site Tools


2019_2020:s3:concprogobjet:td:td6

This is an old revision of the document!


Tests d'intégration et conception en autonomie

Ce TD vise

  1. à vous apprendre à mettre en place des tests d'intégration
  2. à concevoir une petite application à plusieurs en prévoyant les tests d'intégration.
4h c'est à dire 2 séances seront consacrées à ce TD. Vous ne parviendrez peut etre pas à tout faire.
  1. Soyez itératif, faîtes en sorte de toujours avoir un code qui tourne.
  2. Ne trainez pas non plus !

Partie 1 : Tutoriel EasyMock (1h grand maximum)

Si vous dépassez le temps passez à la suite et revenez dessus plus tard. L'énoncé suivant est issu du “getting started” de easyMock mis à jour pour travailler avec junit 5 et voir un peu plus de choses.

  1. Voici le .jar dont vous avez besoin pour avoir accès à l'environnement EasyMock.
  2. Pour l'ajouter à votre classpath du projet : sur le nom du projet > Properties > Java Build Path > Puis ajouter le .jar ci-dessus.
  3. Voici l'interface dont dépendent vos codes, mais que vous ne devez pas implémenter.
    public interface Collaborator {
     
    	// if the document already exists, a "AlreadyAdded" exception is thrown.
    	// if the document fails to be added, false is return.
    	public boolean documentAdded(String title) throws AlreadyAdded   ;
    	public void documentRemoved(String title) ;
     
    }

    Et l'exception associée :

    public class AlreadyAdded extends Exception {
    } 
  4. Voici vos codes (donc vous pouvez bien sûr les modifier si vous le voulez) et donc il s'agit bien de la classe à tester
    public class ClassTested {
     
    	public static  final String COPY = "_copy";
    	private Collaborator listener;
     
    	private HashMap<String, String> documents = new HashMap<>();
     
    	public void setListener(Collaborator listener) {
    		this.listener = listener;
    	}
     
    	public void addDocument(String title, String document) {
    		try {
    			if (listener.documentAdded(title) ) 
    				documents.put(title, document);
    		} catch (AlreadyAdded e) {
    			documents.put(title+COPY, document);
    		}
    	}
     
    	public void removeDocument(String title) {
    		listener.documentRemoved(title);
    	}
     
     
    	public Collaborator getListener() {
    		return listener;
    	}
     
    	public boolean isContained(String key) {
    		return documents.containsKey(key);
     
    	}
    }
  5. Voici un début de tests.
    1. Remarquez la construction du mock (5.a)
       import static org.easymock.EasyMock.*;
      import static org.junit.Assert.assertFalse;
      import static org.junit.jupiter.api.Assertions.assertTrue;
       
      import org.easymock.EasyMockSupport;
      import org.easymock.Mock;
      import org.easymock.TestSubject;
      import org.junit.jupiter.api.BeforeEach;
      import org.junit.jupiter.api.Test;
       
       
       
      public class ExampleTest extends EasyMockSupport {
       
      	  @TestSubject
      	  private ClassTested classUnderTest = new ClassTested(); // 2
       
      	  @Mock
      	  private Collaborator mock; // 1
       
      	  @BeforeEach
      	  public void setUp() {
                      //5.a
      		mock = mock(Collaborator.class); 
      		classUnderTest = new ClassTested();
      		classUnderTest.setListener(mock);
      	  }
       
    2. Vous pouvez l'exécuter il ne se passe rien (il n'y a aucun test 8-) )
  6. Ajoutez un test.
        @Test
    	  public void testRemoveNonExistingDocument() {
    	    // This call should not lead to any notification
    	    // of the Mock Object
    		  replay(mock); // 6.b
    		  classUnderTest.removeDocument("Does not exist");
    	  }
    1. Il échoue en effet on enregistre (6.b) que le mock ne reçoit aucun message alors que là il a reçu un message. Vous devrez donc avoir l'erreur suivante
       java.lang.AssertionError: 
        Unexpected method call Collaborator.documentRemoved("Does not exist"):...
  7. Ajoutez un test, cette fois-ci juste :
    1. On déclare le message que doit recevoir le mock (7.a)
    2. On enregistre (7.b)
    3. On lance le test sur notre code (7.c)
    4. On explicite la vérification (réalisée par défaut) (7.d)
          @Test
      	  public void testRemoveNonExistingDocument2() {
      		  mock.documentRemoved("Does not exist"); //7.a
      		  replay(mock); //7.b
      		  classUnderTest.removeDocument("Does not exist"); //7.c
      		  verify(mock); //7.d
      	  }
  8. Ajoutez un test, mais cette fois-ci nous explicitons la valeur de retour attendue et nous créons deux mocks pour nous assurer qu'il n'y a pas d'effets secondaires
    	  @Test
    	  public void testAddDocument() throws AlreadyAdded {
    		  //Initialisation
    		  Collaborator firstCollaborator = mock(Collaborator.class);//construction de mock
    		  Collaborator  secondCollaborator = mock(Collaborator.class);//construction de mock, il ne sera pas utilisé
    		  classUnderTest.setListener(firstCollaborator); 
     
    		  //// expect document addition
    		  expect(firstCollaborator.documentAdded("New Document")).andReturn(true); //On attend que le mock réponde True ! 
    		  replayAll(); //on enregistre le comportement de tous les Mocks
    		  classUnderTest.addDocument("New Document", "content");
    		  assertTrue(classUnderTest.isContained("New Document")); //on vérifie que NOS codes se comportent correctement
    		  verifyAll(); //On vérifie le comportement de tous les mocks
    	  }
  9. Ajoutez un test, mais cette fois-ci nous attendons pour valeur de retour false
     	  @Test
    	  public void testFailingAddDocument() throws AlreadyAdded {
    		  //Initialisation
    		  Collaborator firstCollaborator = mock(Collaborator.class);
    		  Collaborator secondCollaborator = mock(Collaborator.class);
    		  classUnderTest.setListener(firstCollaborator); 
     
    		  //// expect document addition
    		  expect(firstCollaborator.documentAdded("New Document")).andReturn(false);
    		  replayAll();
    		  classUnderTest.addDocument("New Document", "content");
    		  assertFalse(classUnderTest.isContained("New Document"));
    		  verifyAll();
    	  }
     
  10. et enfin nous testons une levée d'exception
     	  @Test
    	  public void testDuplicationWhenAddingDocument() throws AlreadyAdded {
    		  //Initialisation
    		  Collaborator firstCollaborator = mock(Collaborator.class);
    		  Collaborator secondCollaborator = mock(Collaborator.class);
    		  classUnderTest.setListener(firstCollaborator); 
     
    		  //// expect document addition
    		  expect(firstCollaborator.documentAdded("New Document")).andThrow(new AlreadyAdded());
    		  replayAll();
    		  classUnderTest.addDocument("New Document", "content");
    		  assertTrue(classUnderTest.isContained("New Document"+ClassTested.COPY));
    		  verifyAll();
    	  }

Cela conclut la partie tutorielle. A présent, vous devriez savoir créer vous même des tests d'intégration.

Partie 2 : Un jeu de Quizz

Vous devez réaliser en groupe de X étudiants un jeu de quizz dont voici la spécification.

Spécification

  • V0 : Version de base
    1. En tant que joueur, je veux répondre à une question du quizz
      1. Si j'ai la bonne réponse je gagne 1pt
      2. Si je n'ai pas la bonne réponse, la bonne réponse m'est donnée.
      3. La vérification de la réponse ne dépend pas des minuscules ou majuscules
      4. ex :
        1. “Quelle est la capitale de la France”, je réponds PARIS, le jeu me félicite et m'annonce que j'ai gagné 1 pt
        2. “Quelle est la capitale de l'Espagne”, je réponds madrid … j'ai gagné 1 pt
        3. “Quelle est la capitale de l'Érythrée”, je réponds Assab, le jeu m'annonce que la bonne réponse est Asmara, je n'ai pas gagné de points
    2. En tant que joueur, je veux jouer une partie de quizz, de façon à m'amuser en vérifiant mes connaissances.

Modélisation

Exigences

Un groupe a proposé l'an dernier les interfaces suivantes.

public interface GameInterface {
 
	void startGame();
 
	boolean isNotOver();
 
	String nextQuestion();
 
	boolean setCurrentUserAnswer(String answer);
 
	String getRightAnswer();
 
	int getPoints();
 
}
public interface QuizInterface {
 
	public String getQuestion(int currentQuestionNumber) ;
 
	public boolean isGoodAnswer(int currentQuestionNumber, String answer);
 
	public String getAnswer(int currentQuestionNumber);
 
	public int size();
 
	public void addQuestion(QuestionInterface q1);
 
}
public interface QuestionInterface {
 
	String getAnswer();
 
	String getQuestion();
 
	boolean isGoodAnswer(String answer);
 
}

Voici un code pour tester un jeu.

package quizzPK;
 
import java.util.Scanner;
 
public class mainTestGame {
 
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
 
		QuizInterface quiz = new Quiz();
		QuestionInterface q1 = new Question("Capitale de la France ?", "Paris");
		QuestionInterface q2 = new Question("Capitale de l'Allemagne ?", "Berlin");
		QuestionInterface q3 = new Question("Capitale de l'Italie", "Rome");
 
		quiz.addQuestion(q1);
		quiz.addQuestion(q2);
		quiz.addQuestion(q3);
		GameInterface game = new Game(quiz);
 
		game.startGame();
 
		while (game.isNotOver()) {
			System.out.println(game.nextQuestion() + " : ") ;
			String answer = sc.nextLine();
			boolean valid = game.setCurrentUserAnswer(answer);
			if (!valid ) {
				System.out.println("You failed, the correct answer is : " + game.getRightAnswer() );
			}
			else
				System.out.println("Well done ! ");
		}
 
		System.out.println("Score : " + game.getPoints());
	}
 
 
 
}

A Faire

  1. Modélisez par groupe de 2 ou 3 étudiants, le jeu
    1. En particulier, intéressez-vous aux interactions entre les classes.
    2. Une fois que vous êtes d'accord, vous passez à la suite
  2. Développez séparément
    1. Etudiant 1 : Développez la classe Game sans développer la classe Quiz.
      1. Tester votre classe Game en utilisant des Mocks.
    2. Etudiant 2 : Développez la classe Quizz sans développer la classe Question.
      1. Tester votre classe Quizz en utilisant des Mocks
  3. Intégrez vos codes.

L'essentiel n'est pas d'atteindre cette ultime étape. Mais davantage d'apprendre à écrire des tests d'intégration.

A faire

  1. Conception : V0 (Environ 1/2 heure intense)
    1. Conseils
      • Concevez l'application V0 à deux (i.e. UC, Classes, un diagramme de séquence pour analyser le déroulement d'une partie est conseillé, MAIS ne faîtes que ce qui vous paraît vraiment utile)
      • Partagez le travail à réaliser
      • Prévoyez les tests à réaliser pour chacune des histoires, vous pouvez même faire du TDD
    2. Enrichissez les histoires si besoin pour être sûrs de savoir les tester.
    3. 8-O Sauvegardez une copie de votre conception quelle qu'elle soit : photo ou autre. Vous utiliserez cette conception pour faire le point à la fin entre votre travail initial et la réalisation. Vous ne serez pas noté dessus, vous devez apprendre à vous évaluer.
  2. Vous pouvez à partir de là choisir de focaliser sur une histoire ou de traiter plusieurs histoires en même temps.
    1. Développement : V0
      • Développez le jeu en testant chacun individuellement vos classes et en utilisant les mocks pour les interactions.
    2. Intégration : V0
      • Mettez vos codes en commun
      • Testez une histoire et vérifier que vous pouvez dire qu'elle est terminée, c'est à dire que tous les tests passent.
      • Faîtes les tests sur toutes les histoires, soyez itératif.
    3. 8-O Faîtes une copie de votre architecture;
      1. vous la comparerez avec la précédente et la suivante. Il s'agit ici pour vous de
        1. déterminer si vous aviez fait des erreurs ou des incomplétudes,
        2. déterminer si vous saurez faire mieux la prochaine fois ou non, qu'avez-vous appris éventuellement?
        3. identifier les points que vous aimeriez améliorer (liste des TODO dans le code, le modèle, l'architecture).
  3. V1 : Choisissez une extension, complétez éventuellement les histoires et allez jusqu'au code correspondant.
    1. 8-O reprendre la question précédente.
  4. Si vous êtes ici et que vous en avez envie ajoutez une autre extension.

Quelques trucs utiles

Pour Interroger vous pouvez utiliser:

import javax.swing.JOptionPane;
....
 String m = JOptionPane.showInputDialog("Anyone there?");
 System.out.println(m); // juste pour expliquer ici
 JOptionPane.showMessageDialog(null, "A oui, il y a quelqu'un");

A RENDRE

  1. Document
    1. faisant état de vos réponses aux points 8-O ; les derniers modèles seront considérés pour l'évaluation de l'architecture.
    2. d'une image de la couverture de tests finale
    3. Précisez les extensions réalisées.
  2. Des codes et tests associés.

Rendus au plus tard le lundi 19 novembre à 18h45 sous http://jalon.unice.fr/cours/blay/Cours-blay-20150930110548/BoiteDepot-blay-20181113101132476393?mode_etudiant=true&tab=deposit

Voici le document qui sera utilisé pour l'évaluation (il peut encore changer) : https://goo.gl/forms/PmNPOPcurIKJLlo32

2019_2020/s3/concprogobjet/td/td6.1572727077.txt.gz · Last modified: 2019/11/02 21:37 by blay