Chapitre 1. Le problème de l'argent
Je ne donnerais pas une figue pour la simplicité de ce côté-ci de la complexité, mais je donnerais ma vie pour la simplicité de l'autre côté de la complexité.
Oliver Wendell Holmes Jr.
Notre environnement de développement est prêt. Dans ce chapitre, nous allons apprendre les trois phases qui soutiennent le développement piloté par les tests. Nous écrirons ensuite notre première fonctionnalité de code en utilisant le développement piloté par les tests.
Rouge-Vert-Refactor : Les éléments constitutifs du TDD
Le développement piloté par les tests suit un processus en trois phases :
-
Rouge. Nous écrivons un test qui échoue (y compris les éventuels échecs de compilation). Nous exécutons la suite de tests pour vérifier les tests défaillants.
-
Vert. Nous écrivons juste assez de code de production pour que le test soit vert. Nous exécutons la suite de tests pour le vérifier.
-
Remaniement. Nous supprimons les codes qui sentent mauvais. Celles-ci peuvent être dues à des doublons, à des valeurs codées en dur ou à une mauvaise utilisation des idiomes du langage (par exemple, l'utilisation d'une boucle verbeuse au lieu d'un itérateur intégré). Si des tests sont interrompus pendant le remaniement, la priorité est de les remettre au vert avant de quitter cette phase.
Il s'agit du cycle rouge-vert-réfacteur (RGR), illustré à la figure 1-1. Les trois phases de ce cycle sont les éléments essentiels du développement piloté par les tests. Tout le code que nous développerons dans ce livre suivra ce cycle.
Quel est le problème ?
Nous avons un problème d'argent. Non, pas celui que presque tout le monde a : ne pas en avoir assez ! C'est plutôt un problème de "nous voulons suivre notre argent".
Disons que nous devons construire une feuille de calcul pour gérer de l'argent dans plus d'une devise, peut-être pour gérer un portefeuille d'actions.
Stock | Bourse | Actions | Prix de l'action | Total |
---|---|---|---|---|
IBM |
NASDAQ |
100 |
124 USD |
12400 USD |
BMW |
DAX |
400 |
75 EUR |
30000 EUR |
Samsung |
KSE |
300 |
68000 KRW |
20400000 KRW |
Pour construire cette feuille de calcul, nous devons effectuer des opérations arithmétiques simples sur des nombres dans une devise donnée :
5 USD × 2 = 10 USD |
10 EUR × 2 = 20 EUR |
4002 KRW / 4 = 1000,5 KRW |
Nous aimerions aussi pouvoir faire des conversions entre les monnaies. Par exemple, si en échangeant 1 EUR, on obtient 1,2 USD, et en échangeant 1 USD, on obtient 1100 KRW :
5 USD + 10 EUR = 17 USD |
1 USD + 1100 KRW = 2200 KRW |
Chacun des points susmentionnés sera une (toute petite) fonctionnalité que nous mettrons en œuvre en utilisant le TDD. Nous avons déjà plusieurs fonctionnalités à mettre en œuvre. Pour nous aider à nous concentrer sur une seule chose à la fois, nous mettrons en gras la fonctionnalité sur laquelle nous travaillons. Lorsque nous en aurons terminé avec une fonctionnalité, nous signalerons notre satisfaction enla barrant.
Alors, par où commencer ? Au cas où le titre de ce livre ne serait pas un signe évident, nous commencerons par écrire un test.
Notre premier test d'échec
Commençons par mettre en place la toute première fonctionnalité de notre liste :
5 USD × 2 = 10 USD |
10 EUR × 2 = 20 EUR |
4002 KRW / 4 = 1000,5 KRW |
5 USD + 10 EUR = 17 USD |
1 USD + 1100 KRW = 2200 KRW |
Nous allons commencer par écrire un test qui échoue, correspondant à la phase rouge du cycle RGR.
Go
Dans un nouveau fichier appelé money_test.go
dans le dossier go
, écrivons notre premier test :
package
main
import
(
"testing"
)
func
TestMultiplication
(
t
*
testing
.
T
)
{
fiver
:=
Dollar
{
amount
:
5
,
}
tenner
:=
fiver
.
Times
(
2
)
if
tenner
.
amount
!=
10
{
t
.
Errorf
(
"Expected 10, got: [%d]"
,
tenner
.
amount
)
}
}
Déclaration de l'emballage
Paquet "testing" importé, utilisé plus tard dans
t.Errorf
Notre méthode de test, qui doit commencer par
Test
et avoir un argument*testing.T
La structure représentant "USD 5."
Dollar
n'existe pas encoreMéthode testée :
Times
- qui n'existe pas encore.Comparer la valeur réelle à la valeur attendue
S'assurer que le test échoue si la valeur attendue n'est pas égale à la valeur réelle.
Cette fonction de test comprend un peu de code passe-partout.
Le package main
déclare que tout le code qui en découle fait partie du paquetage main
. Il s'agit d'une exigence pour les programmes Go exécutables autonomes. La gestion des paquets est une fonctionnalité sophistiquée de Go. Elle est abordée plus en détail au chapitre 5.
Ensuite, nous importons le paquetage testing
à l'aide de l'instruction import
. Ce paquetage sera utilisé dans le test unitaire.
Le test unitaire func
tion constitue l'essentiel du code. Nous déclarons une entité représentant "5 USD". Il s'agit de la variable nommée fiver
, que nous initialisons à une struct contenant 5 dans son champ amount
. Ensuite, nous multiplions fiver
par 2. Et nous nous attendons à ce que le résultat soit 10 dollars, c'est-à-dire une variable tenner
dont le champ amount
doit être égal à 10. Si ce n'est pas le cas, nous imprimons un message d'erreur bien formaté avec la valeur réelle (quelle qu'elle soit).
Lorsque nous exécutons ce test en utilisant "go test -v .
" à partir du dossier racine du projet TDD, nous devrions obtenir une erreur :
...
undefined
:
Dollar
FAIL
tdd
[
build
failed
]
FAIL
Nous recevons le message fort et clair : c'est notre premier test d'échec !
Astuce
"go test -v .
" exécute les tests dans le dossier actuel, et "go test -v ./...
"1 exécute les tests dans le dossier en cours et dans tous les sous-dossiers. Le commutateur -v
produit une sortie verbale.
JavaScript
Dans un nouveau fichier appelé test_money.js
dans le dossier js
, écrivons notre premier test :
const
assert
=
require
(
'assert'
)
;
let
fiver
=
new
Dollar
(
5
)
;
let
tenner
=
fiver
.
times
(
2
)
;
assert
.
strictEqual
(
tenner
.
amount
,
10
)
;
Importer le paquet
assert
, nécessaire pour l'assertion plus tardL'objet représentant "USD 5."
Dollar
n'existe pas encoreMéthode testée :
times
- qui n'existe pas encore.Comparaison de la valeur réelle et de la valeur attendue dans une instruction assert
strictEqual
JavaScript a un minimum de code boilerplate - la seule ligne en plus du code de test est l'instruction require
. Cela nous donne accès au paquet NPM assert
.
Après cette ligne se trouvent les trois lignes de code qui constituent notre test. Nous créons un objet représentant 5 USD, nous le multiplions par 2 et nous nous attendons à ce que le résultat soit 10.
Important
ES2015 a introduit le mot-clé let
pour déclarer les variables et le mot-clé const
pour déclarer les constantes.
Lorsque nous exécutons ce code à partir du dossier racine du projet TDD à l'aide de node js/test_money.js
, nous devrions obtenir une erreur qui commence comme ceci :
ReferenceError
:
Dollar
is
not
defined
C'est notre premier test qui a échoué. Hourra !
Astuce
node file.js
exécute le code JavaScript dans file.js
et produit un résultat. Nous utilisons cette commande pour exécuter nos tests.
Python
Dans un nouveau fichier appelé test_money.py
dans le dossier py
, écrivons notre premier test :
import
unittest
class
TestMoney
(
unittest
.
TestCase
)
:
def
testMultiplication
(
self
)
:
fiver
=
Dollar
(
5
)
tenner
=
fiver
.
times
(
2
)
self
.
assertEqual
(
10
,
tenner
.
amount
)
if
__name__
==
'
__main__
'
:
unittest
.
main
(
)
Importation du paquet
unittest
, nécessaire pour la superclasseTestCase
.Notre classe de test, qui doit sous-classer la classe
unittest.TestCase
.Le nom de notre méthode doit commencer par
test
pour être considéré comme une méthode de test.L'objet représentant "USD 5."
Dollar
n'existe pas encore.Méthode testée :
times
- qui n'existe pas encore.Comparer la valeur réelle à la valeur attendue dans une déclaration
assertEqual
.L'idiome
main
garantit que cette classe peut être exécutée en tant que script.
Python exige import
ing le paquetage unittest
, la création d'une classe qui sous-classe TestCase
, et def
ining une fonction dont le nom commence par test
. Pour pouvoir exécuter la classe en tant que programme autonome, nous avons besoin de l'idiome commun de Python qui exécute la fonction unittest.main()
lorsque test_money.py
est exécuté directement.
La fonction de test décrit comment nous attendons que notre code fonctionne. Nous définissons une variable nommée fiver
et l'initialisons à une classe souhaitée (mais qui n'a pas encore été créée) Dollar
avec 5
comme argument du constructeur. Nous multiplions ensuite fiver
par 2
et stockons le résultat dans une variable tenner
. Enfin, nous nous attendons à ce que le amount
de tenner
soit 10
.
Lorsque nous exécutons ce code à partir du dossier TDD_PROJECT_ROOT
en utilisant python3 py/test_money.py -v
, nous obtenons une erreur :
NameError
:
name
'Dollar'
is
not
defined
C'est notre premier test qui a échoué. Hourra !
Go pour le vert
Nous avons écrit nos tests comme nous nous attendions à ce qu'ils fonctionnent, en ignorant allègrement toutes les erreurs de syntaxe pour le moment. Est-ce intelligent ?
Au tout début - c'est-à-dire là où nous en sommes - il est intelligent de commencer par le plus petit bout de code qui nous met sur la voie du progrès. Bien sûr, nos tests échouent parce que nous n'avons pas défini ce qu'est Dollar
. C'est peut-être le moment idéal pour dire "Duh !". Cependant, il faut faire preuve d'un peu de patience pour ces deux raisons :
-
Nous venons de terminer la première étape - passer au rouge - denotre premier test. Ce n'est pas seulement le début, c'est le tout début du début.
-
Nous pouvons (et nous allons) accélérer les paliers au fur et à mesure que nous avançons. Cependant, il est important de savoir que nous pouvons ralentir lorsque c'est nécessaire.
La phase suivante du cycle RGR consiste à passer au vert.
Il est clair que nous devons introduire une abstraction Dollar
. Cette section définit comment introduire cette abstraction, ainsi que d'autres abstractions, pour que notre test réussisse.
Go
Ajoute un Dollar struct
vide à la fin de money_test.go
.
type
Dollar
struct
{
}
Lorsque nous exécutons le test maintenant, nous obtenons une nouvelle erreur :
...
unknown
field
'
amount
'
in
struct
literal
of
type
Dollar
Progrès !
Le message d'erreur nous demande d'introduire un champ nommé amount
dans notre structure Dollar
. C'est ce que nous allons faire, en utilisant pour l'instant un type de données int
(ce qui est suffisant pour notre objectif) :
type
Dollar
struct
{
amount
int
}
L'ajout du site Dollar struct
, de façon assez prévisible, nous amène à l'erreur suivante :
...
fiver
.
Times
undefined
(
type
Dollar
has
no
field
or
method
Times
)
Nous voyons ici un schéma : lorsqu'il y a quelque chose (un champ ou une méthode) qui n'est pas défini, nous obtenons cette erreur undefined
de la part du runtime de Go. Nous utiliserons cette information pour accélérer nos cycles TDD à l'avenir. Pour l'instant, ajoutons une func
tion nommée Times
. Nous savons, d'après la façon dont nous avons écrit notre test, que cette fonction doit prendre un nombre (le multiplicateur) et renvoyer un autre nombre (le résultat).
Mais comment calculer le résultat ? Nous connaissons l'arithmétique de base : comment multiplier deux nombres. Mais si nous devions écrire le code le plus simple qui fonctionne, nous serions justifiés de toujours renvoyer le résultat que notre test attend, c'est-à-dire une struct représentant 10 dollars:
func
(
d
Dollar
)
Times
(
multiplier
int
)
Dollar
{
return
Dollar
{
10
}
}
Lorsque nous exécutons notre code maintenant, nous devrions obtenir une réponse courte et douce sur notre terminal:
==
=
RUN
TestMultiplication
---
PASS
:
TestMultiplication
(
0.00
s
)
PASS
C'est le mot magique : nous avons fait notre test PASS
!
JavaScript
Dans test_money.js
, juste après la ligne const assert = require('assert');
, définis une classe vide nommée Dollar
:
class
Dollar
{
}
Lorsque nous exécutons maintenant le fichier test_money.js
, nous obtenons une erreur :
TypeError
:
fiver
.
times
is
not
a
function
Progrès ! L'erreur indique clairement qu'il n'y a pas de fonction nommée times
définie pour l'objet nommé fiver
. Nous allons donc l'introduire dans la classe Dollar
:
class
Dollar
{
times
(
multiplier
)
{
}
}
L'exécution du test produit maintenant une nouvelle erreur :
TypeError
:
Cannot
read
properties
of
undefined
(
reading
'amount'
)
Notre test attend un objet avec une propriété amount
. Comme notre méthode times
ne renvoie rien, la valeur de retour est undefined
, qui n'a pas de propriété amount
(ni aucune autre propriété, d'ailleurs).
Astuce
Dans le langage JavaScript, les fonctions et les méthodes ne déclarent pas explicitement de type de retour. Si nous examinons le résultat d'une fonction qui ne renvoie rien, nous constaterons que la valeur de retour est undefined
.
Alors comment faire pour que notre test passe au vert ? Quelle est la chose la plus simple qui pourrait fonctionner ? Et si nous créions toujours un objet représentant 10 USD et que nous le renvoyions ?
Essayons-le. Nous ajoutons un constructor
qui initialise les objets à un montant donné et une méthode times
qui crée et renvoie obstinément des objets "10 USD" :
class
Dollar
{
constructor
(
amount
)
{
this
.
amount
=
amount
;
}
times
(
multiplier
)
{
return
new
Dollar
(
10
)
;
}
}
La fonction
constructor
est appelée chaque fois qu'un objetDollar
est créé.Initialise la variable
this.amount
avec le paramètre donné.La méthode
times
prend un paramètre.Mise en œuvre simple : toujours rendre 10 dollars.
Lorsque nous exécutons notre code maintenant, nous ne devrions pas obtenir d'erreurs. C'est notre premier test vert !
Important
Comme strictEqual
et d'autres méthodes du paquetage assert
ne produisent des résultats que lorsque les assertions échouent, un test réussi sera assez silencieux et ne produira aucun résultat. Nous améliorerons ce comportement au chapitre 6.
Python
Puisque 'Dollar' is not defined
, définissons dans test_money.py
avant notre classe TestMoney
:
class
Dollar
:
pass
Lorsque nous exécutons maintenant notre code, nous obtenons une erreur :
TypeError
:
Dollar
()
takes
no
arguments
Progrès ! L'erreur nous indique clairement qu'il n'y a actuellement aucun moyen d'initialiser des objets avec des arguments, tels que et que nous avons dans notre code. Dollar
avec des arguments, comme les objets 5
et 10
que nous avons dans notre code. Nous allons donc résoudre ce problème en fournissant l'initialisation la plus brève possible :
class
Dollar
:
def
__init__
(
self
,
amount
):
pass
Le message d'erreur de notre test change maintenant :
AttributeError
:
'Dollar'
object
has
no
attribute
'times'
Nous voyons ici un schéma : notre test échoue toujours, mais pour des raisons légèrement différentes à chaque fois. Au fur et à mesure que nous définissons nos abstractions - d'abord Dollar
puis un champ amount
- les messages d'erreur "s'améliorent" pour passer à l'étape suivante. C'est l'une des caractéristiques du TDD : des progrès constants à un rythme que nous contrôlons.
Accélérons un peu les choses en définissant une fonction times
et en lui donnant le comportement minimum pour passer au vert. Quel est le comportement minimum nécessaire ? Toujours renvoyer un objet "dix dollars" qui est requis par notre test, bien sûr !
class
Dollar
:
def
__init__
(
self
,
amount
)
:
self
.
amount
=
amount
def
times
(
self
,
multiplier
)
:
return
Dollar
(
10
)
La fonction
__init__
est appelée chaque fois qu'un objetDollar
est créé.Initialise la variable
self.amount
avec le paramètre donné.La méthode
times
prend un paramètre.La mise en œuvre simple consiste à toujours rendre 10 dollars.
Lorsque nous exécutons notre test maintenant, nous obtenons une réponse courte et douce :
Ran
1
test
in
0.000
s
OK
Il est possible que le test ne fonctionne pas sur 0.000s
, mais ne perdons pas de vue le mot magique OK
. C'est notre premier test vert !
Nettoyer
Tu te sens déconcerté par le fait que nous nous soyons mis au vert en codant en dur "10 USD" dans nos tests ? Ne t'inquiète pas : l'étape du remaniement nous permet de remédier à ce malaise en cherchant comment nous pouvons supprimer la valeur codée en dur et dupliquée de "10 USD".
Leremaniement est la troisième et dernière étape du cycle RGR. Il se peut que nous n'ayons pas beaucoup de lignes de code à ce stade ; cependant, il est toujours important de garder les choses bien rangées et compactes. Si nous avons des problèmes de mise en forme ou des lignes de code commentées, c'est le moment de les nettoyer.
La nécessité de supprimer les doublons et de rendre le code lisible est plus importante. À première vue, il peut sembler que dans la vingtaine de lignes de code que nous avons écrites, il ne peut pas y avoir de doublons. Cependant, il y a déjà un peu de duplication subtile mais significative.
Nous pouvons trouver cette duplication en remarquant quelques bizarreries dans notre code :
-
Nous avons écrit juste assez de code pour vérifier que "doubler 5 dollars devrait nous donner 10 dollars". Si nous décidons de modifier notre test existant pour dire "doubler 10 dollars devrait nous donner 20 dollars" - une affirmation tout aussi sensée - nous devrons modifier à la fois notre test et notre code
Dollar
. Il existe une dépendance, un couplage logique, entre les deux segments de code. En général, ce type de couplage doit être évité. -
Dans notre test et notre code, nous avions le nombre magique
10
. Où avons-nous trouvé ce chiffre ? Nous avons évidemment fait le calcul dans notre tête. Nous nous rendons compte qu'en doublant 5 dollars, nous devrions obtenir 10 dollars. Nous avons donc écrit10
dans notre test et dans notre code.Dollar
code. Nous devrions nous rendre compte que le10
dans l'entitéDollar
est en réalité5 * 2
. Cette prise de conscience nous permettrait de supprimer cette duplication.
Le code dupliqué est souvent le symptôme d'un problème sous-jacent : une abstraction de code manquante ou un mauvais couplage entre les différentes parties du code.2
Supprimons la duplication et, par conséquent, débarrassons-nous également du couplage.
Go
Remplace le 10
dans la fonction Times
par son équivalent 5 * 2
:
func
(
d
Dollar
)
Times
(
multiplier
int
)
Dollar
{
return
Dollar
{
5
*
2
}
}
Le test doit toujours être vert.
En l'écrivant de cette façon, nous nous rendons compte de l'abstraction manquante. Le code dur 5
est en réalité d.amount
, et le 2
est le multiplier
. En remplaçant ces nombres codés en dur par les variables correctes, nous obtenons l'implémentation non triviale :
func
(
d
Dollar
)
Times
(
multiplier
int
)
Dollar
{
return
Dollar
{
d
.
amount
*
multiplier
}
}
Et voilà ! Le test passe toujours, et nous avons supprimé la duplication et le couplage.
Il y a un dernier nettoyage à faire.
Dans notre test, nous avons explicitement utilisé le nom de champ amount
lors de l'initialisation d'une structure Dollar
. Il est également possible d'omettre les noms de champs lors de l'initialisation d'une structure, comme nous l'avons fait dans notre méthode Times
.3 L'un ou l'autre style - utiliser des noms explicites ou ne pas les utiliser - fonctionne. Cependant, il est important d'être cohérent. Modifions la fonction Times
pour spécifier le nom du champ :
func
(
d
Dollar
)
Times
(
multiplier
int
)
Dollar
{
return
Dollar
{
amount
:
d
.
amount
*
multiplier
}
}
JavaScript
Remplaçons le 10
de la méthode times
par son équivalent 5 * 2
:
times
(
multiplier
)
{
return
new
Dollar
(
5
*
2
);
}
Le test doit toujours être vert.
L'abstraction manquante est maintenant claire. Nous pouvons remplacer 5
par this.amount
et 2
par multiplier
:
times
(
multiplier
)
{
return
new
Dollar
(
this
.
amount
*
multiplier
);
}
Bravo ! Le test est toujours vert, et nous avons éliminé à la fois le double 10
et le couplage.
Python
Remplaçons le 10
dans la méthode times
par son équivalent 5 * 2
:
def
times
(
self
,
multiplier
):
return
Dollar
(
5
*
2
)
Le test reste vert, comme prévu.
Cela révèle l'abstraction sous-jacente. Le 5
est en réalité le self.amount
et le 2
est le multiplier
:
def
times
(
self
,
multiplier
):
return
Dollar
(
self
.
amount
*
multiplier
)
Hourra ! Le test reste vert, et la duplication et le couplage ont disparu.
Engager nos changements
Nous avons terminé notre première fonctionnalité en utilisant le TDD. Pour ne pas l'oublier, il est important de valider notre code dans notre référentiel de contrôle de version à intervalles fréquents.
Un test vert est un excellent endroit pour livrer du code.
Dans une fenêtre de l'interpréteur de commandes, tapons ces deux commandes :
git
add
.
git
commit
-m
"feat: first green test"
Ajoute tous les fichiers, y compris toutes les modifications qu'ils contiennent, à l'index Git.
Livrer l'index Git au référentiel avec le message donné.
En supposant que le code pour les trois langues existe dans les bons dossiers, nous devrions obtenir un message comme celui-ci.
[
main
(
root-commit
)
bb31b94
]
feat:
first
green
test
4
files
changed,
56
insertions
(
+
)
create
mode
100644
go/go.mod
create
mode
100644
go/money_test.go
create
mode
100644
js/test_money.js
create
mode
100644
py/test_money.py
Le nombre hexagonal,
bb31b94
, représente les premiers chiffres du "hachage SHA" unique associé au commit. Il sera différent pour chaque personne (et chaque commit).
Cela indique que tous nos fichiers sont en sécurité dans notre dépôt de contrôle de version Git. Nous pouvons le vérifier en exécutant la commande git log
sur notre shell, qui devrait produire une sortie similaire à la suivante :
commit
bb31b94e90029ddeeee89f3ca0fe099ea7556603
(
HEAD
->
main
)
Author:
Saleem
Siddiqui
...
Date:
Sun
Mar
7
12:26:06
2021
-0600
feat:
first
green
test
Voici le premier commit, avec son hachage SHA complet.
Voici le message que nous avons tapé pour notre première livraison.
Il est important de réaliser que le dépôt Git dans lequel nous avons commis notre code réside également sur notre système de fichiers local. (Il se trouve dans le dossier .git
sous notre TDD_PROJECT_ROOT
). Bien que cela ne nous mette pas à l'abri d'un renversement accidentel de café sur notre ordinateur (utilise toujours des couvercles), cela nous donne l'assurance que nous pouvons revenir à une version antérieure connue et bonne si nous nous emmêlons les pinceaux quelque part. Dans le chapitre 13, nous pousserons tout notre code dans un dépôt GitHub.
Nous utiliserons cette stratégie de validation de notre code dans notre dépôt Git local dans chaque chapitre, en utilisant le même ensemble de commandes.
Important
Nous utiliserons les deux commandes git add .
et git commit -m _commit message_
pour valider fréquemment notre code dans chaque chapitre.
La seule chose qui variera est le message de livraison, qui suivra le style de livraison sémantique et inclura une courte description d'une ligne des changements.
Astuce
Les messages git commit
de ce livre suivent le style de l'engagement sémantique.
Où nous sommes
Ce chapitre a introduit le développement piloté par les tests en montrant le tout premier cycle rouge-vert-réfacteur. Notre première petite fonctionnalité ayant été mise en œuvre avec succès, rayons-la de la liste. Voici où nous en sommes dans notre liste de fonctionnalités :
5 USD × 2 = 10 USD |
10 EUR × 2 = 20 EUR |
4002 KRW / 4 = 1000,5 KRW |
5 USD + 10 EUR = 17 USD |
1 USD + 1100 KRW = 2200 KRW |
Prenons un moment pour revoir et savourer notre code avant de passer au défi suivant. Le code source des trois langues est reproduit ci-dessous. Il est également accessible dans le dépôt GitHub. Par souci de concision, les prochains chapitres ne mentionneront que le nom de la branche correspondante.
Go
Voici à quoi ressemble le fichier money_test.go
à l'heure actuelle :
package
main
import
(
"testing"
)
func
TestMultiplication
(
t
*
testing
.
T
)
{
fiver
:=
Dollar
{
amount
:
5
}
tenner
:=
fiver
.
Times
(
2
)
if
tenner
.
amount
!=
10
{
t
.
Errorf
(
"Expected 10, got: [%d]"
,
tenner
.
amount
)
}
}
type
Dollar
struct
{
amount
int
}
func
(
d
Dollar
)
Times
(
multiplier
int
)
Dollar
{
return
Dollar
{
amount
:
d
.
amount
*
multiplier
}
}
JavaScript
Voici à quoi ressemble le fichier test_money.js
à ce stade :
const
assert
=
require
(
'assert'
);
class
Dollar
{
constructor
(
amount
)
{
this
.
amount
=
amount
;
}
times
(
multiplier
)
{
return
new
Dollar
(
this
.
amount
*
multiplier
);
}
}
let
fiver
=
new
Dollar
(
5
);
let
tenner
=
fiver
.
times
(
2
);
assert
.
strictEqual
(
tenner
.
amount
,
10
);
Python
Voici à quoi ressemble le fichier test_money.py
à l'heure actuelle :
import
unittest
class
Dollar
:
def
__init__
(
self
,
amount
):
self
.
amount
=
amount
def
times
(
self
,
multiplier
):
return
Dollar
(
self
.
amount
*
multiplier
)
class
TestMoney
(
unittest
.
TestCase
):
def
testMultiplication
(
self
):
fiver
=
Dollar
(
5
)
tenner
=
fiver
.
times
(
2
)
self
.
assertEqual
(
10
,
tenner
.
amount
)
if
__name__
==
'__main__'
:
unittest
.
main
()
Astuce
Le code de ce chapitre se trouve dans une branche nommée "chap01" dans le dépôt GitHub. Il existe une branche pour chaque chapitre dans lequel le code est développé.
Dans le chapitre 2, nous allons accélérer les choses en développant quelques fonctionnalités supplémentaires.
1 Les trois points dans "go test -v ./...
" et "go fmt ./...
" doivent être tapés littéralement ; ce sont les seuls cas dans ce livre où ils ne représentent pas un code omis !
2 L'opinion de Kent Beck mérite d'être citée ici : "Si la dépendance est le problème, la duplication est le symptôme".
3 S'il y a plusieurs champs dans la structure - ce qui n'est pas le cas actuellement - alors soit l 'ordre des champs doit être le même dans la définition et l'initialisation de la structure , soit les noms des champs doivent être spécifiés lors de l'initialisation de la structure. Voir https://gobyexample.com/structs.
Get Apprendre le développement piloté par les tests now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.