Skip to content

Latest commit

 

History

History
489 lines (364 loc) · 18 KB

File metadata and controls

489 lines (364 loc) · 18 KB

Objectifs

  • Un développeur front doit pouvoir mocker les appels serveur lors de ses tests.
  • Un développeur full-stack doit pouvoir tester unitairement son code serveur.
  • Un développeur doit pouvoir tester (test d'intégration ou test fonctionnel) son code avec un contexte d'exécution propre entre chaque test.
  • Un développeur doit pouvoir spécifier dans son test, l'état dans lequel se trouve son application avant l'exécution du test.

Objectifs à plus long terme

  • Un développeur doit pouvoir exécuter ses tests en parallèle

Pré-requis

  • J'utilise mon éditeur de texte ou IDE préféré.
  • J'ai une application front qui est prête à utiliser les cloud services. (Projet initialisé, dépendances installées, éventuellement la configuration de saisie)
  • J'utilise mon framework de test préféré. Dans les exemples de syntaxe, nous utiliserons Mocha.
  • Nous allons utiliser sinon.js pour réaliser des mocks et des stubs dans nos tests.
  • Le cas d'usage de nos Users Stories sera un chat temps réel entre des personnages Avengers.

Parcours 1 : Je développe une application front avec ZetaPush sans custom cloud service

User Stories

[P01-TEST01] ETQ dev je mock un appel à un cloud service

celtia-beta-1

GIVEN

  • Je suis en cours de développement de mon Avengers Chat
  • J'ai importé le cloud service UserService qui me permet de gérer des utilisateurs
  • J'ai importé le cloud service SearchService qui me permet de sauvegarder dans un moteur de recherche des données
  • J'ai accès à UserService via l'objet userService
  • J'ai accès à SearchService via l'objet searchService
  • Je souhaite tester mon code front qui me permet de créer un Avenger, pour ceci je mock mes différents appels aux Cloud Services pour tester uniquement mon code front
  • J'ai une fonction côté front qui intègre ma logique métier :
addAvengers(avenger) {
  // Create a new user using the UserService Cloud Service
  const newUser = await this.userService.createUser({
    "username": avenger.name, // This field will be displayed in the chat
    "firstname": avenger.realIdentity.firstname,
    "lastname": avenger.realIdentity.lastname
  });

  // Add the id of the created user in our Avenger
  avenger.id = newUser.id;

  // Store the Avengers in search engine to get it from his superpowers
  for (let superpower of avenger.superpowers) {
    // We only ask to store the data, we don't use the response
    this.searchService.index({ type: "avengers", index: "avengers-by-superpowers", "id": newUser.id+'-'+superpower.name, "data": { superpower: superpower.name, avenger: avenger}});
  }
  return avenger
}
  • Nous utilisons sinon.js pour mocker les appels aux Cloud Services.

    var assert = require("assert");
    var sinon = require("sinon");
    
    /**
     *    Test to check if a new user is correctly added to the array of all users
     */
    describe("Users", function() {
      describe("#checkUserAdded()", function() {
        it("User should be in allUsers array", async function() {
    
          /**
           *  Use Add stub on UserService and SearchService to avoid call to Cloud Services
           */
          const createUserStub = sinon.stub(this.userService, "createUser");
          createUserStub.withArgs({username: "Spiderman", firstname: "Peter", lastname: "Parker"}).returns({username: "Spiderman", firstname: "Peter", lastname: "Parker", id: "7682164"});
          // We only mock SearchService because we don't care about the result
          const searchMock = sinon.mock(this.searchService);
    
          /**
           *  We call our function to add a new Avengers
           */
          addAvenger({
            name: "Spiderman", 
            realIdentity: {firstname: "Peter", lastname: "Parker", profession: "freelance photographer"},
            superpowers: ["web-shooters", "spider-sense"]);
          });
    
          /**
           *  We check if the avenger is correctly added in the search engine
           */
          searchMock.expects("index").twice();
    
          /**
           *  Ensure that we have the correct ID
           */
          assert.notNull(avenger.id);
      });
    });

WHEN

  • Lorsque je lance l'exécution de mon test

THEN

  • L'appel à la cloud function ne se fait pas, un objet prédéfini est renvoyé en réponse conformément à l'utilisation de sinon.js
  • Pour le code front, le mock de la cloud function est complètement transparent, le comportement (d'un point de vue front) est le même hormis la réponse renvoyée.

[P01-TEST02] ETQ dev je souhaite pouvoir appliquer à mon test un état de mon application précédemment sauvegardé

celtia-beta-1

GIVEN

  • Je suis en cours de développement de mon Avengers Chat
  • J'ai importé le cloud service UserService qui me permet de gérer des utilisateurs
  • J'ai importé le cloud service SearchService qui me permet de sauvegarder dans un moteur de recherche des données
  • J'ai accès à UserService via l'objet userService
  • J'ai accès à SearchService via l'objet searchService
  • J'ai importé le cloud service UtilsService qui me permet d'utiliser des cloud functions utilitaires de ZetaPush
  • J'ai accès à UtilsService via l'objet utilsService
  • Je souhaite donner un état particulier à mon application côté back. Pour ceci je spécifie un snapshot de mon application.
  • Je souhaite tester mon code front qui me permet de créer un Avenger, pour ceci je mock mes différents appels aux Cloud Services pour tester uniquement mon code front
  • J'ai un état précédent de mon application sauvegardé côté back, que je peux retrouver avec le nom 3-avengers-already-created
  • J'ai une fonction côté front qui intègre ma logique métier :
addAvengers(avenger) {
  // Create a new user using the UserService Cloud Service
  const newUser = await this.userService.createUser({
    "username": avenger.name, // This field will be displayed in the chat
    "firstname": avenger.realIdentity.firstname,
    "lastname": avenger.realIdentity.lastname
  });

  // Add the id of the created user in our Avenger
  avenger.id = newUser.id;

  // Store the Avengers in search engine to get it from his superpowers
  for (let superpower of avenger.superpowers) {
    // We only ask to store the data, we don't use the response
    this.searchService.index({ type: "avengers", index: "avengers-by-superpowers", "id": newUser.id+'-'+superpower.name, "data": { superpower: superpower.name, avenger: avenger}});
  }
  return avenger
}
  • Nous utilisons sinon.js pour tester mocker les appels aux Cloud Services.

    var assert = require("assert");
    var sinon = require("sinon");
    
    /**
     *    Test to check if a new user is correctly added to the array of all users
     */
    describe("Users", function() {
      describe("#checkUserAdded()", function() {
        it("User should be in allUsers array", async function() {
    
          /**
           *    I specify the name of my snapshot. For this, I use the "setSnapshotName()" cloud function from the Utils cloud service.
           */
          await this.utilsService.setSnapshotName("3-avengers-already-created");
    
    
          /**
           *  Use Add stub on UserService and SearchService to avoid call to Cloud Services
           */
          const createUserStub = sinon.stub(this.userService, "createUser");
          createUserStub.withArgs({username: "Spiderman", firstname: "Peter", lastname: "Parker"}).returns({username: "Spiderman", firstname: "Peter", lastname: "Parker", id: "7682164"});
          // We only mock SearchService because we don't care about the result
          const searchMock = sinon.mock(this.searchService);
    
          /**
           *  We call our function to add a new Avengers
           */
          addAvenger({
            name: "Spiderman", 
            realIdentity: {firstname: "Peter", lastname: "Parker", profession: "freelance photographer"},
            superpowers: ["web-shooters", "spider-sense"]);
          });
    
          /**
           *  We check if the avenger is correctly added in the search engine
           */
          searchMock.expects("index").twice();
    
          /**
           *  Ensure that we have the correct ID
           */
          assert.notNull(avenger.id);
      });
    });

WHEN

  • Lorsque je lance l'exécution de mon test

THEN

  • Mon test s'est lancé en prenant en compte l'état de mon application précédemment spécifié, ici un état où 3 utilisateurs sont déjà créés dans mon application côté back.
  • On peut spécifier l'état de l'application de 3 manières :
    • this.utilsService.setSnapshotName(name-of-remote-snapshot);
    • this.utilsService.setSnapshotFile(path/to/local/snap);
    • this.utilsService.setToScratch();

[P01-TEST03] ETQ dev je teste mon application dans un environnement vierge

celtia-beta-1

GIVEN

  • Je suis en cours de développement de mon Avengers Chat
  • J'ai importé le cloud service UserService qui me permet de gérer des utilisateurs
  • J'ai importé le cloud service SearchService qui me permet de sauvegarder dans un moteur de recherche des données
  • J'ai accès à UserService via l'objet userService
  • J'ai accès à SearchService via l'objet searchService
  • J'ai importé le cloud service UtilsService qui me permet d'utiliser des cloud functions utilitaires de ZetaPush
  • J'ai accès à UtilsService via l'objet utilsService
  • Je souhaite donner un état particulier à mon application côté back. Pour ceci je spécifie un snapshot de mon application.
  • Je souhaite tester mon code front qui me permet de créer un Avenger, pour ceci je mock mes différents appels aux Cloud Services pour tester uniquement mon code front
  • Je souhaite revenir à un état précédent de mon application, où la plateforme côté back est vierge
  • J'ai une fonction côté front qui intègre ma logique métier :
addAvengers(avenger) {
  // Create a new user using the UserService Cloud Service
  const newUser = await this.userService.createUser({
    "username": avenger.name, // This field will be displayed in the chat
    "firstname": avenger.realIdentity.firstname,
    "lastname": avenger.realIdentity.lastname
  });

  // Add the id of the created user in our Avenger
  avenger.id = newUser.id;

  // Store the Avengers in search engine to get it from his superpowers
  for (let superpower of avenger.superpowers) {
    // We only ask to store the data, we don't use the response
    this.searchService.index({ type: "avengers", index: "avengers-by-superpowers", "id": newUser.id+'-'+superpower.name, "data": { superpower: superpower.name, avenger: avenger}});
  }
  return avenger
}
  • Nous utilisons sinon.js pour mocker les appels aux Cloud Services.

    var assert = require("assert");
    var sinon = require("sinon");
    
    /**
     *    Test to check if a new user is correctly added to the array of all users
     */
    describe("Users", function() {
      describe("#checkUserAdded()", function() {
        it("User should be in allUsers array", async function() {
    
          /**
           *    I specify that I want to go to state "from scratch" of my application
           */
          await this.utilsService.setToScratch();
    
    
          /**
           *  Use Add stub on UserService and SearchService to avoid call to Cloud Services
           */
          const createUserStub = sinon.stub(this.userService, "createUser");
          createUserStub.withArgs({username: "Spiderman", firstname: "Peter", lastname: "Parker"}).returns({username: "Spiderman", firstname: "Peter", lastname: "Parker", id: "7682164"});
          // We only mock SearchService because we don't care about the result
          const searchMock = sinon.mock(this.searchService);
    
          /**
           *  We call our function to add a new Avengers
           */
          addAvenger({
            name: "Spiderman", 
            realIdentity: {firstname: "Peter", lastname: "Parker", profession: "freelance photographer"},
            superpowers: ["web-shooters", "spider-sense"]);
          });
    
          /**
           *  We check if the avenger is correctly added in the search engine
           */
          searchMock.expects("index").twice();
    
          /**
           *  Ensure that we have the correct ID
           */
          assert.notNull(avenger.id);
      });
    });

WHEN

  • Lorsque je lance l'exécution de mon test

THEN

  • Mon test s'est lancé en prenant en compte l'état de mon application précédemment spécifié, ici un état où 3 utilisateurs sont déjà créés dans mon application côté back.
  • On peut spécifier l'état de l'application de 3 manières :
    • this.utilsService.setSnapshotName(name-of-remote-snapshot);
    • this.utilsService.setSnapshotFile(path/to/local/snap);
    • this.utilsService.setToScratch();

Parcours 2 Je développe une application avec ZetaPush et des custom cloud services

User Stories

[P02-TEST01] ETQ dev full-stack je teste un custom cloud service

celtia-alpha-2

GIVEN

  • Je suis en cours de développement de mon Avengers Chat
  • J'ai importé le cloud service UserService qui me permet de gérer des utilisateurs
  • J'ai accès à UserService via l'objet userService
  • J'ai un custom cloud service de créé et de déployé nommé AvengersService et qui comporte la cloud function attackWithRandomSkill() :
class AvengersService {

  /**
   *  Function to print that an Avenger do a random attack
   */
  attackWithRandomSkill(avengerName) {
    // Get the Avengers
    const avenger = this.userService.getUserByLogin({login: avengerName}):

    // Get a random power of this Avenger
    const superpower = avenger.superpowers[Math.floor(Math.random()*avenger.superpowers.length)];

    // Print that we attack with a random skill !
    print(`${avengerName} attacks with ${superpower} !!!`);

    return superpower;
  }
}
  • Je souhaite tester unitairement le fonctionnement de attackWithRandomSkill()

  • J'ai écrit le test unitaire suivant :

    /**
     *    Test to check if "attackWithRandomSkill()" is correctly working
     */
    describe("HealthData", function() {
      describe("#checkAttackWithRandomSkill()", function() {
        it("The cloud function should return a specific object", async function() {
    
    
          /**
           *  I create one Avenger
           */
          addAvenger({
            name: "Spiderman", 
            realIdentity: {firstname: "Peter", lastname: "Parker", profession: "freelance photographer"},
            superpowers: ["web-shooters", "spider-sense"]);
          });
    
          /**
           *  I attack with this user
           */
          const resultAttack = attackWithRandomSkill("Spiderman");
          
    
    
           /**
            * Check if the response is not null
            */
          assert.notNull(resultAttack);

WHEN

  • Lorsque je lance l'exécution de mon test

THEN

  • Je reçois en réponse le retour de mon test conformément au framework utilisé
  • Les éventuelles actions réalisées côté back sont effectives sur l'application (Pas de contexte neuf de spécifié)

[P02-TEST02] ETQ dev full-stack je teste un custom cloud service dans un nouveau contexte

GIVEN

  • Je suis en cours de développement de mon Avengers Chat
  • J'ai importé le cloud service UserService qui me permet de gérer des utilisateurs
  • J'ai accès à UserService via l'objet userService
  • J'ai importé le cloud service UtilsService qui me permet d'utiliser des cloud functions utilitaires de ZetaPush
  • J'ai accès à UtilsService via l'objet utilsService
  • Je souhaite que les actions côté serveur n'affecte pas mon application et que j'exécute mon application dans un nouveau contexte "jetable"
  • J'ai un custom cloud service de créé et de déployé nommé AvengersService et qui comporte la cloud function attackWithRandomSkill() :
class AvengersService {

  /**
   *  Function to print that an Avenger do a random attack
   */
  attackWithRandomSkill(avengerName) {
    // Get the Avengers
    const avenger = this.userService.getUserByLogin({login: avengerName}):

    // Get a random power of this Avenger
    const superpower = avenger.superpowers[Math.floor(Math.random()*avenger.superpowers.length)];

    // Print that we attack with a random skill !
    print(`${avengerName} attacks with ${superpower} !!!`);

    return superpower;
  }
}
  • Je souhaite tester unitairement le fonctionnement de attackWithRandomSkill()

  • J'ai écrit le test unitaire suivant :

    /**
     *    Test to check if "attackWithRandomSkill()" is correctly working
     */
    describe("HealthData", function() {
      describe("#checkAttackWithRandomSkill()", function() {
        it("The cloud function should return a specific object", async function() {
          /**
           * I specify that I use a new context to execute my test
           */
          await this.utilsService.workInDisposableContext();
    
          /**
           *  I create one Avenger
           */
          addAvenger({
            name: "Spiderman", 
            realIdentity: {firstname: "Peter", lastname: "Parker", profession: "freelance photographer"},
            superpowers: ["web-shooters", "spider-sense"]);
          });
    
          /**
           *  I attack with this user
           */
          const resultAttack = attackWithRandomSkill("Spiderman");
          
    
    
           /**
            * Check if the response is not null
            */
          assert.notNull(resultAttack);

WHEN

  • Lorsque je lance l'exécution de mon test

THEN

  • Je reçois en réponse le retour de mon test conformément au framework utilisé
  • Le test s'est exécuté dans un contexte neuf et jetable
  • Les éventuelles actions réalisées côté back n'ont pas affecté l'application