TP 1

Que faut-il retenir des tests unitaires Angular ?

Est-ce que angular met en avant une techno (un outil) plus qu’une autre ?

Angular ne met pas en avant une technologie plus qu’une autre pour les tests unitaires. Cependant, il utilise Jasmine comme framework de test par défaut.

Faut-il ajouter des modules spécifiques pour faire des tests unitaires avec Jasmine ?

Pour faire des tests unitaires avec Jasmine, il n’est pas nécessaire d’ajouter de modules spécifiques. Jasmine est inclus dans les dépendances de développement lors de la création d’un projet Angular.

Quels sont les éléments clés pour gérer des tests avec Jasmine sous angular ?

Pour utiliser Jasmine, il est préférable de savoir les éléments suivants :

  • Utiliser la commande "ng test" pour exécuter les tests.

  • Créer un fichier de test séparé pour chaque fichier source à tester.

  • Importer les classes ou les composants à tester.

  • Utiliser les fonctions Jasmine telles que describe(), beforeEach(), afterEach(), it(), expect(), etc., pour écrire les tests.

J’ai créé un test pour la méthode codeName() :

Il est dans le fichier :

Le test est dans le fichier personne.spec.ts.

Voici le contenu de ce test :

it('should get a correct code with method codeName()', () => {
  personne = new Personne('Robert');
  expect(personne.codeName()).toEqual('Robertxxxx');
});

Voici le contenu de cette méthode (que j’ai ajouté dans la classe Personne )

codeName(): string {
  return (this.nom + 'x'.repeat(10)).substring(0, 10);
}

J’ai créé un test pour la méthode onChangeId()

Il est dans le fichier :

Le test est dans le fichier personne-item.component.spec.ts.

Voici le contenu de ce test :

it('should change personne.id when use onChangeId() method', () => {
  const before = component.personne.id;
  component.onChangeId();
  const after = component.personne.id;

  expect(before).not.toBe(after);
});

TP 2

Que faut-il retenir des tests de bout en bout avec Cypress pour Angular ?

Est-ce que Angular met en avant une techno (un outil) plus qu’une autre ?

Angular n’impose pas un outil de test spécifique pour les tests End-to-End. Toutefois, Cypress est un choix populaire en raison de sa simplicité et de son efficacité.

Faut-il ajouter des modules spécifiques pour faire des tests End-to-End avec Cypress ?

Il n’y a pas besoin de modules spécifiques pour faire des tests End-to-End avec Cypress. Cypress est déjà intégré à Angular et peut être utilisé dès la création du projet.

Quels sont les éléments clés pour gérer des tests avec Cypress sous Angular ?

Les éléments clés pour gérer des tests avec Cypress sous Angular sont les suivants :

  • La configuration : Il est important de configurer correctement Cypress pour Angular en utilisant @cypress/angular/plugins.

  • La gestion des dépendances : Il est nécessaire d’ajouter les dépendances spécifiques pour Angular et Cypress dans le fichier package.json.

  • L’écriture des tests : Il est important d’écrire des tests end-to-end avec Cypress qui simulent les interactions de l’utilisateur avec l’application, en se concentrant sur les cas d’utilisation les plus importants.

  • L’utilisation de Selectors : Les Selectors sont des identifiants uniques qui permettent de cibler des éléments spécifiques dans l’application. Il est important d’utiliser des Selectors pour s’assurer que les tests sont fiables et ne sont pas affectés par les modifications de l’interface utilisateur.

  • L’analyse des résultats des tests : Il est important d’analyser les résultats des tests pour identifier les erreurs et les échecs de test, afin de pouvoir les corriger rapidement.

En suivant ces éléments clés, il est possible de gérer efficacement des tests avec Cypress sous Angular.

J’ai créé un test pour le fonctionnement du bouton reset dans mon premier projet :

Il est dans le fichier…​

Le test pour le fonctionnement du bouton reset est dans le fichier spec.cy.ts.

Voici le contenu de ce test (juste votre test…​ pas le fichier entier)

it('Réinitialiser le compteur', () => {
  cy.get('#compteur').should('have.text','1')
  const rand = Math.random()*49 + 1
  for (let index = 0; index < rand; index++) {
    cy.get("#oneUp").click()
  }
  cy.get('#compteur').should('not.have.text','1')
  cy.get("#reset").click()
  cy.get('#compteur').should('have.text','0')
})

Voici le contenu de la vue (avec les balise que j’ai éventuellement modifié)

<h1>{{title}}</h1>
<div>
 Points : <span id="compteur">{{points}}</span>
</div>
<button id="oneUp" (click)="onPlus()">Plus 1</button>
<button id="reset" (click)="onReset()">Reset</button>

J’ai créé 5 tests pour la Tour des Héros, pour chacun :

Voici ce que font les 5 tests créés pour la Tour des Héros :

Teste si la page d’accueil contient le titre "Tour of Heroes"
Teste si la page du tableau de bord contient le titre "Top Heroes"
Teste si la page du tableau de bord contient le titre "Hero Search"
Teste si le détail d’un héros sélectionné est correctement affiché
Teste si un nouveau héros peut être ajouté à la liste des héros

Voici le code des 5 tests créés pour la Tour des Héros :

describe('Tour of Heroes App Testing', () => {
beforeEach(()=>{
})
it(Should have the title 'Tour Of Heroes', () => {
cy.visit('/')
cy.contains('Tour of Heroes')
})
it(Should have title 'Top Heroes', () => {
cy.visit('/dashboard')
cy.contains('Top Heroes')
})
it(Should have title 'Hero Search', () => {
cy.visit('/dashboard')
cy.contains('Hero Search')
})
it (Should select and route to (${targetHero}) Hero details,
dashboardSelecTargetHero)
it(Back to home', () => {
cy.visit('/')
cy.contains('Tour of Heroes')
})
it (Should add (${newHeroName}) in Heroes list,
heroesAddHero)
})

Voici le code commenté des 5 tests créés pour la Tour des Héros :

// Teste si la page d'accueil contient le titre "Tour of Heroes"
describe('Tour of Heroes App Testing', () => {
beforeEach(()=>{
})
it(Should have the title 'Tour Of Heroes', () => {
cy.visit('/')
cy.contains('Tour of Heroes')
})

// Teste si la page du tableau de bord contient le titre "Top Heroes"
it(`Should have title 'Top Heroes'`, () => {
    cy.visit('/dashboard')
    cy.contains('Top Heroes')
})

// Teste si la page du tableau de bord contient le titre "Hero Search"
it(`Should have title 'Hero Search'`, () => {
    cy.visit('/dashboard')
    cy.contains('Hero Search')
})

// Teste si le détail d'un héros sélectionné est correctement affiché
it (`Should select and route to (${targetHero}) Hero details`,
dashboardSelecTargetHero)

// Teste si on peut retourner à la page d'accueil depuis n'importe quelle page
it(`Back to home'`, () => {
    cy.visit('/')
    cy.contains('Tour of Heroes')
})

// Teste si on peut ajouter un nouveau héros à la liste des héros
it (`Should add (${newHeroName}) in Heroes list`,
heroesAddHero)
})

J’ai trouvé les 3 cas d’utilisation suivants qui ne sont pas encore testés pour la Tour des Héros :

  1. Ajouter un héros à partir de la page des héros

  2. Modifier les détails d’un héros existant

  3. Supprimer un héros existant

Il est important de créer des tests pour ces cas d’utilisation afin de s’assurer que l’application fonctionne correctement dans ces scénarios.

TP 3

Description des plans de tests envisagés avec le nom des tests prévus :

Plan de test pour la liste des tâches :

  • Vérification que la page d’accueil affiche la bonne quantité de tâches

  • Vérification que la liste des tâches contient bien toutes les tâches créées

  • Vérification que la liste des tâches s’affiche bien sous forme de tableau

Plan de test pour la création de tâches :

  • Vérification que l’utilisateur peut créer une nouvelle tâche

  • Vérification que la nouvelle tâche créée est ajoutée à la liste des tâches existantes

  • Vérification que la nouvelle tâche créée contient bien toutes les informations saisies par l’utilisateur

Plan de test pour la suppression de tâches :

  • Vérification que l’utilisateur peut supprimer une tâche existante

  • Vérification que la tâche supprimée n’apparaît plus dans la liste des tâches

  • Vérification que les autres tâches restent inchangées après la suppression de la tâche

Plan de test pour la modification de tâches :

  • Vérification que l’utilisateur peut modifier une tâche existante

  • Vérification que la tâche modifiée est bien mise à jour dans la liste des tâches

  • Vérification que la tâche modifiée contient bien toutes les informations modifiées par l’utilisateur

Code des plans de test réalisés :

Plan de test pour la liste des tâches :

describe('Liste des tâches', () => {
it('Affichage de la bonne quantité de tâches sur la page d'accueil', () => {
cy.visit('/taches');
cy.get('[data-testid="nbtaches"]').should('have.text','4');
});
it('Affichage de toutes les tâches créées', () => {
cy.visit('/taches');
cy.get('[data-testid="tache"]').should('have.length', 4);
});
it('Affichage de la liste des tâches sous forme de tableau', () => {
cy.visit('/taches');
cy.get('[data-testid="tableau"]').should('exist');
});
});

Plan de test pour la création de tâches :

describe('Création de tâches', () => {
it('Ajout d'une nouvelle tâche à la liste', () => {
cy.visit('/taches');
cy.get('[data-testid="titre"]').type('Nouvelle tâche');
cy.get('[data-testid="description"]').type('Description de la nouvelle tâche');
cy.get('[data-testid="ajouter"]').click();
cy.get('[data-testid="tache"]').should('have.length', 5);
});
it('Affichage des informations de la nouvelle tâche créée', () => {
cy.visit('/taches');
cy.get('[data-testid="titre"]').type('Nouvelle tâche');
cy.get('[data-testid="description"]').type('Description de la nouvelle tâche');
cy.get('[data-testid="ajouter"]').click();
cy.get('[data-testid="tache"]').last().within(() => {
cy.get('td').first().should('have.text', 'Nouvelle tâche');
cy.get('td').eq(1).should('have.text', 'Description de la nouvelle tâche');
cy.get('td').eq(2).should('have.text', 'En attente');
});
});

it('Impossible de créer une tâche sans titre', () => {
cy.visit('/taches');
cy.get('[data-testid="description"]').type('Description de la nouvelle tâche');
cy.get('[data-testid="ajouter"]').click();
cy.get('[data-testid="erreur"]').should('have.text', 'Le titre est obligatoire');
});

it('Impossible de créer une tâche avec un titre déjà existant', () => {
cy.visit('/taches');
cy.get('[data-testid="titre"]').type('Tâche 1');
cy.get('[data-testid="description"]').type('Description de la nouvelle tâche');
cy.get('[data-testid="ajouter"]').click();
cy.get('[data-testid="erreur"]').should('have.text', 'Une tâche avec ce titre existe déjà');
});
});

Plan de test pour la modification de tâches :

describe('Modification de tâches', () => {
it('Modification du titre d'une tâche existante', () => {
cy.visit('/taches');
cy.get('[data-testid="tache"]').first().within(() => {
cy.get('[data-testid="modifier"]').click();
cy.get('[data-testid="titre"]').clear().type('Nouveau titre');
cy.get('[data-testid="enregistrer"]').click();
});
cy.get('[data-testid="tache"]').first().within(() => {
cy.get('td').first().should('have.text', 'Nouveau titre');
});
});

it('Modification de la description d'une tâche existante', () => {
cy.visit('/taches');
cy.get('[data-testid="tache"]').first().within(() => {
cy.get('[data-testid="modifier"]').click();
cy.get('[data-testid="description"]').clear().type('Nouvelle description');
cy.get('[data-testid="enregistrer"]').click();
});
cy.get('[data-testid="tache"]').first().within(() => {
cy.get('td').eq(1).should('have.text', 'Nouvelle description');
});
});

it('Modification de l'état d'une tâche existante', () => {
cy.visit('/taches');
cy.get('[data-testid="tache"]').first().within(() => {
cy.get('[data-testid="modifier"]').click();
cy.get('[data-testid="etat"]').select('En cours');
cy.get('[data-testid="enregistrer"]').click();
});
cy.get('[data-testid="tache"]').first().within(() => {
cy.get('td').eq(2).should('have.text', 'En cours');
});
});
});