Défi

Nois développons une application d'e-commerce. Il est possible d'acheter les produits grâce à un porte-monnaie virtuel inclus dans l'application. Voici les fonctions de l'application dans un programme montrant comment elles peuvent être utilisées :

1
// On définit une classe représentant un panier
2
class Basket {
3
  constructor (items = [], totalPrice = 0) {
4
    this.items = items
5
    this.totalPrice = totalPrice
6
  }
7
}
8
9
function addToBasket (basket, item) {
10
  basket.items.push(item)
11
  basket.totalPrice = basket.totalPrice + item.price
12
}
13
14
function removeFromBasket (basket, item) {
15
  for (let i = 0; i < basket.items.length; i++) {
16
    if (JSON.stringify(item) === JSON.stringify(basket.items[i])) {
17
      basket.items.splice(i, 1)
18
      basket.totalPrice = basket.totalPrice - item.price
19
      break
20
    }
21
  }
22
}
23
24
function transactionAllowed (userAccount, priceToPay) {
25
  if (userAccount.balance >= priceToPay) {
26
    return true
27
  }
28
  return false
29
}
30
31
function payBasket (userAccount, basket) {
32
  if (transactionAllowed(userAccount, basket.totalPrice)) {
33
    userAccount.balance = userAccount.balance - basket.totalPrice
34
    console.log('Paiement du panier réussi')
35
  } else {
36
    console.log('Paiement du panier échoué')
37
  }
38
}
39
40
const currentBasket = new Basket()
41
42
const item1 = { name: 'Carte mère', price: 100 }
43
const item2 = { name: 'Carte graphique', price: 300 }
44
const user = { name: 'Perceval', balance: 500 }
45
addToBasket(currentBasket, item1)
46
addToBasket(currentBasket, item2)
47
48
// Plus qu'un produit dans le panier
49
removeFromBasket(currentBasket, item1)
50
console.log(currentBasket)
51
payBasket(user, currentBasket)
52
console.log(user)
53
// Perceval n'a plus que 200 euros

Question

Développer un test unitaire qui ajoute un produit au panier et vérifie que le montant est bien celui prévu. Ce test sera nommé testAdd().

Indice

Par exemple :

  • Ajouter un produit dans le panier, comme :

1
const item = {name: "Carte mère", price: 100}
  • Vérifier que basket.totalPrice vaut 100 :

Solution

1
function testAdd () {
2
  const testBasket = new Basket()
3
  const item = { name: 'Carte mère', price: 100 }
4
  addToBasket(testBasket, item)
5
  if (testBasket.totalPrice !== 100) {
6
    console.log('Test ajout échoué')
7
    return false
8
  }
9
  console.log('Test ajout réussi')
10
  return true
11
}
12
13
testAdd()

Question

Développer un test unitaire qui supprime un produit du panier et vérifie que le montant est bien celui prévu. Ce test sera nommé testRemove().

Indice

Il faudra au préalable ajouter un produit dans un panier vide avant de pouvoir retirer celui-ci pour tester le retrait.

Par exemple :

  • Ajouter un objet au panier ;

  • Le retirer immédiatement ;

  • Vérifier que le montant total du panier est nul.

Solution

1
function testRemove () {
2
  const testBasket = new Basket()
3
  const item = { name: 'Carte mère', price: 100 }
4
  addToBasket(testBasket, item)
5
6
  removeFromBasket(testBasket, item)
7
  if (testBasket.totalPrice !== 0) {
8
    console.log('Test retrait échoué lors du premier retrait')
9
    return false
10
  }
11
  console.log('Test retrait réussi')
12
  return true
13
}
14
15
testRemove()

Question

On constate qu'il est possible de factoriser les tests unitaires des fonctions d'ajout et de retrait précédentes.

Donner le test factorisé nommé testAddRemove().

Indice

Le test de retrait se base sur l'ajout d'un produit. Il est donc possible de tester d'abord l'ajout d'un produit, puis son retrait, au sein de la même fonction.

Solution

1
function testAddRemove () {
2
  const testBasket = new Basket()
3
  const item = { name: 'Carte mère', price: 100 }
4
  addToBasket(testBasket, item)
5
  if (testBasket.totalPrice !== 100) {
6
    console.log("Test ajout-retrait échoué lors de l'ajout")
7
    return false
8
  }
9
10
  removeFromBasket(testBasket, item)
11
  if (testBasket.totalPrice !== 0) {
12
    console.log('Test ajout-retrait échoué lors du premier retrait')
13
    return false
14
  }
15
16
  console.log('Test ajout-retrait réussi')
17
  return true
18
}
19
20
testAddRemove()

Question

Donner maintenant un test unitaire qui teste entièrement la fonction transactionAllowed(). La fonction de test s'appellera testTransactionAllowed().

Indice

Il faut que chacune des branches de la condition soit vérifiée, donc il faudra appeler deux fois la fonction testée : une fois pour un solde suffisant, une fois pour un solde insuffisant.

Par exemple :

  • Ajouter un utilisateur avec un solde de 500 € ;

  • Vérifier qu'une transaction de 400 € est autorisée ;

  • Vérifier qu'une transaction de 600 € est interdite.

Solution

1
function testTransactionAllowed () {
2
  const testUser = { name: 'Perceval', balance: 500 }
3
  if (!transactionAllowed(testUser, 400)) {
4
    console.log('Test transactionAllowed échoué pour 400')
5
    return false
6
  }
7
  if (transactionAllowed(testUser, 600)) {
8
    console.log('Test transactionAllowed échoué pour 600')
9
    return false
10
  }
11
  console.log('Test transactionAllowed réussi')
12
  return true
13
}
14
15
testTransactionAllowed()

Question

Écrire un test fonctionnel pour le règlement d'un panier qu'on nommera testPayBasket(). Le test vérifiera que le solde de l'utilisateur est bien mis à jour après règlement.

Indice

Pour réaliser ce test fonctionnel, il faut créer un panier puis essayer de régler deux fois le panier. La première fois, la transaction doit réussir. La seconde fois, la transaction doit échouer car l'utilisateur n'a plus assez d'argent, donc son solde ne doit pas être mis à jour.

Par exemple :

  • Ajouter un utilisateur avec un solde de 500 € ;

  • Ajouter un produit à son panier valant 300 € ;

  • Effectuer le règlement et vérifier que son solde est passé à 200 € ;

  • Essayer d'effectuer un deuxième règlement et vérifier que son solde reste à 200 €.

Solution

1
function testPayBasket () {
2
  const testUser = { name: 'Perceval', balance: 500 }
3
  let testBasket = new Basket()
4
  testBasket = addToBasket(testBasket, { name: 'Carte mère', price: 300 })
5
6
  payBasket(testUser, testBasket)
7
  // Paiement réussi
8
  if (testUser.balance !== 200) {
9
    console.log('Test régler panier échoué lors de la première transaction')
10
    return false
11
  }
12
13
  payBasket(testUser, testBasket)
14
  // Paiement échoué car le solde n'a pas changé
15
  if (testUser.balance !== 200) {
16
    console.log('Test régler panier échoué lors de la première transaction')
17
    return false
18
  }
19
20
  console.log('Test de la fonctionnalité de règlement du panier réussi')
21
  return true
22
}
23
24
testPayBasket()

Question

Écrire une fonction testAppEcommerce() qui lance successivement tous les tests. Cette fonction affichera « OK » si tous les tests sont passés, et « ERREUR » sinon.

Écrire le programme complet permettant de réaliser tous les tests.

Indice

Tous les tests retournent true ou false.

Indice

Il est possible de faire des opérations booléennes :

1
const success = true && false // Ici, on utilise l'opérateur logique ET

On pourra alors vérifier que l'intégralité des tests a renvoyé true, en chaînant les &&.

Indice

1
function testAppEcommerce () {
2
  let success = testAddRemove()
3
  success = success && testTransactionAllowed()
4
  success = success && testPayBasket()
5
  if (success) {
6
    console.log('OK')
7
  } else {
8
    console.log('ERREUR')
9
  }
10
}

Solution

1
// On définit une classe représentant un panier
2
class Basket {
3
  constructor (items = [], totalPrice = 0) {
4
    this.items = items
5
    this.totalPrice = totalPrice
6
  }
7
}
8
9
function addToBasket (basket, item) {
10
  basket.items.push(item)
11
  basket.totalPrice = basket.totalPrice + item.price
12
}
13
14
function removeFromBasket (basket, item) {
15
  for (let i = 0; i < basket.items.length; i++) {
16
    if (JSON.stringify(item) === JSON.stringify(basket.items[i])) {
17
      basket.items.splice(i, 1)
18
      basket.totalPrice = basket.totalPrice - item.price
19
      break
20
    }
21
  }
22
}
23
24
function transactionAllowed (userAccount, priceToPay) {
25
  if (userAccount.balance >= priceToPay) {
26
    return true
27
  }
28
  return false
29
}
30
31
function payBasket (userAccount, basket) {
32
  if (transactionAllowed(userAccount, basket.totalPrice)) {
33
    userAccount.balance = userAccount.balance - basket.totalPrice
34
    console.log('Paiement du panier réussi')
35
  } else {
36
    console.log('Paiement du panier échoué')
37
  }
38
}
39
40
function testAddRemove () {
41
  const testBasket = new Basket()
42
  const item = { name: 'Carte mère', price: 100 }
43
  addToBasket(testBasket, item)
44
  if (testBasket.totalPrice !== 100) {
45
    console.log("Test ajout-retrait échoué lors de l'ajout")
46
    return false
47
  }
48
49
  removeFromBasket(testBasket, item)
50
  if (testBasket.totalPrice !== 0) {
51
    console.log('Test ajout-retrait échoué lors du premier retrait')
52
    return false
53
  }
54
55
  console.log('Test ajout-retrait réussi')
56
  return true
57
}
58
59
function testTransactionAllowed () {
60
  const testUser = { name: 'Perceval', balance: 500 }
61
  if (!transactionAllowed(testUser, 400)) {
62
    console.log('Test transactionAllowed échoué pour 400')
63
    return false
64
  }
65
  if (transactionAllowed(testUser, 600)) {
66
    console.log('Test transactionAllowed échoué pour 600')
67
    return false
68
  }
69
  console.log('Test transactionAllowed réussi')
70
  return true
71
}
72
73
function testPayBasket () {
74
  const testUser = { name: 'Perceval', balance: 500 }
75
  const testBasket = new Basket()
76
  addToBasket(testBasket, { name: 'Carte mère', price: 300 })
77
78
  payBasket(testUser, testBasket)
79
  // Paiement réussi
80
  if (testUser.balance !== 200) {
81
    console.log('Test régler panier échoué lors de la première transaction')
82
    return false
83
  }
84
85
  payBasket(testUser, testBasket)
86
  // Paiement échoué car le solde n'a pas changé
87
  if (testUser.balance !== 200) {
88
    console.log('Test régler panier échoué lors de la deuxième transaction')
89
    return false
90
  }
91
92
  console.log('Test de la fonctionnalité de règlement du panier réussi')
93
  return true
94
}
95
96
function testAppEcommerce () {
97
  let success = testAddRemove()
98
  success = success && testTransactionAllowed()
99
  success = success && testPayBasket()
100
  if (success) {
101
    console.log('OK')
102
  } else {
103
    console.log('ERREUR')
104
  }
105
}
106
107
testAppEcommerce()
108