Test fonctionnel

Objectif

  • Savoir écrire un test fonctionnel.

Mise en situation

Les tests fonctionnels testent directement les fonctionnalités d'un programme du point de vue de l'utilisateur. Pour cela, au lieu de tester une fonction du code source, on va tester une fonctionnalité du programme, par exemple que l'inscription fonctionne correctement sur un site web. Cette manière de tester est moins ancrée dans la technique que les tests unitaires, et plus orientée vers l'expérience utilisateur. On veut s'assurer ici que l'on n'introduit pas de régressions dans le fonctionnement de notre logiciel.

RappelTest fonctionnel

Le but d'un test fonctionnel est de vérifier le bon fonctionnement d'une fonctionnalité complète. Le test sera effectué sur une boite noire, c'est-à-dire que les opérations intermédiaires effectuées par la fonctionnalité sont ignorées. Ainsi, contrairement au test unitaire qui étudie le déroulé d'une composante, le test fonctionnel étudie le comportement d'un programme. Il s'agit donc d'un test plus global qui ne sera pas aussi exhaustif que le test unitaire.

MéthodeComment tester fonctionnellement ?

Pour tester fonctionnellement, il y a deux étapes : le fonctionnement normal et le fonctionnement accidentel.

  • Le fonctionnement normal assure que la fonctionnalité se comporte correctement dans les grandes lignes sans essayer tous les cas de figures (la combinatoire est en général trop importante).

  • Le fonctionnement accidentel essaie des scénarios anormaux qui pourraient faire planter le programme.

FondamentalProgramme résistant aux accidents

Un programme que les tests de fonctionnement accidentel ne font pas planter, sera considéré comme un programme résistant aux accidents.

Il arrive également de croiser une terminologie anglaise plus négativement connotée : idiot-proof program qui fait référence à l'idiotie supposée des entrées testées.

AttentionLoi de Murphy

Selon la Loi de Murphy, Tout ce qui est susceptible d'aller mal, ira mal. Elle est également vraie en programmation car les utilisateurs se trompent parfois dans l'utilisation d'une application. Cette recherche passera très souvent par un usage totalement illogique de la fonctionnalité. Ainsi, tester des cas de figures ridicules permettra d'éviter ce type d'erreurs pouvant parfois être utilisés pour attaquer une application. Par exemple, un utilisateur pourrait entrer « -18 » dans la case « Age » pour voir si le programme plantera à cause de l'âge inférieur à 0.

Exemple

Voici un exemple simplifié de test fonctionnel de transfert d'argent d'une application bancaire.

Ici, deux cas sont essayés :

  1. transférer avec assez de solde,

  2. et transférer sans avoir assez de solde.

Le test fonctionnel sert ici à valider que le transfert échoue si le solde est insuffisant.

1
/** JavaScript : teste fonctionnellement le transfert d'argent. */
2
function checkAccount (account, amount) {
3
  if (account.balance >= amount) {
4
    return true
5
  }
6
  return false
7
}
8
9
function transfer (srcAccount, tgtAccount, amount) {
10
  // Copies des variables
11
  const newSrcAccount = { ...srcAccount }
12
  const newTgtAccount = { ...tgtAccount }
13
  if (checkAccount(newSrcAccount, amount)) {
14
    newSrcAccount.balance = newSrcAccount.balance - amount
15
    newTgtAccount.balance = newTgtAccount.balance + amount
16
    console.log('Transfert réussi')
17
  } else {
18
    console.log('Echec du transfert')
19
  }
20
  return [newSrcAccount, newTgtAccount]
21
}
22
23
function testTransfer () {
24
  let srcAcc = { owner: 'Jean Dupont', balance: 100 }
25
  let tgtAcc = { owner: 'Anne Martin', balance: 20 }
26
27
  let transferRes = transfer(srcAcc, tgtAcc, 60)
28
  srcAcc = transferRes[0]
29
  tgtAcc = transferRes[1]
30
  // Transfert réussi
31
  if (srcAcc.balance !== 40 || tgtAcc.balance !== 80) {
32
    console.log('Test transfer échoué lors du premier transfert.')
33
    return false
34
  }
35
36
  transferRes = transfer(srcAcc, tgtAcc, 60)
37
  srcAcc = transferRes[0]
38
  tgtAcc = transferRes[1]
39
  // Transfert échoué donc les comptes doivent être inchangés
40
  if (srcAcc.balance !== 40 || tgtAcc.balance !== 80) {
41
    console.log('Test transfer échoué lors du second transfert.')
42
    return false
43
  }
44
  console.log('Test réussi')
45
  return true
46
}
47
48
testTransfer()
49
1
"""Python : teste fonctionnellement le transfert d'argent. """
2
def check_account(account, amount):
3
  if account["balance"] >= amount:
4
    return True
5
  else:
6
    return False
7
8
def transfer(src_account, tgt_account, amount):
9
  new_src_account = src_account.copy()
10
  new_tgt_account = tgt_account.copy()
11
  if check_account(new_src_account, amount):
12
    new_src_account["balance"] = new_src_account["balance"] - amount
13
    new_tgt_account["balance"] = new_tgt_account["balance"] + amount
14
    print("Transfert réussi")
15
  else:
16
    print("Echec du transfert")
17
  return new_src_account, new_tgt_account
18
19
20
def test_transfer():
21
  src_acc = {"owner": "Jean Dupont", "balance": 100}
22
  tgt_acc = {"owner": "Anne Martin", "balance": 20}
23
  src_acc, tgt_acc = transfer(src_acc, tgt_acc, 60)
24
  # Transfert réussi
25
  if src_acc["balance"] != 40 or tgt_acc["balance"] != 80:
26
    print("Test transfer échoué lors du premier transfert.")
27
    return False
28
29
  src_acc, tgt_acc = transfer(src_acc, tgt_acc, 60)
30
  # Transfert échoué donc les comptes doivent être inchangés
31
  if src_acc["balance"] != 40 or tgt_acc["balance"] != 80:
32
    print("Test transfer échoué lors du premier transfert.")
33
    return False
34
35
  print("Test réussi")
36
  return True
37
38
test_transfer()

RappelTester les entrées/sorties

Tester les entrées/sorties d'un programme est souvent une tâche complexe.

  • Ces tests sont facilités par les frameworks de test.

  • Les développeurs ont recours à des mocks, qui simulent des réponses de bases de données ou de serveurs.

ComplémentTest d'intégration

Un test d'intégration vérifie que la communication de plusieurs fonctions se fait comme prévue. Il contrôle les assemblages de fonctions.

À retenir

  • Les tests fonctionnels sont des tests globaux qui testent le comportement d'une fonctionnalité.

  • Il est nécessaire de tester des valeurs correctes ainsi que des valeurs intentionnellement fausses afin que les fonctionnalités soient résistantes aux usages imprévus.

  • Pour développer des tests pour des projets de grande envergure il est nécessaire d'utiliser un framework de test.