Chapitre 1. Le problème de l'argent

Cet ouvrage a été traduit à l'aide de l'IA. Tes réactions et tes commentaires sont les bienvenus : translation-feedback@oreilly.com

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 :

  1. 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.

  2. 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.

  3. 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.

The Red Green Refactor (RGR) cycle. Red: write a failing test. Green: write just enough production code to pass the test. Refactor: remove code smells, e.g. duplication or poor design, without adding new features.
Figure 1-1. Le cycle rouge-vert-réfacteur est le fondement sur lequel repose le développement piloté par les tests
Important

Les trois phases du cycle rouge-vert-réfacteur sont les éléments essentiels du TDD.

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 1

import (
    "testing" 2
)

func TestMultiplication(t *testing.T) { 3
    fiver := Dollar{ 4
        amount: 5,
    }
    tenner := fiver.Times(2) 5
    if tenner.amount != 10 { 6
        t.Errorf("Expected 10, got: [%d]", tenner.amount) 7
    }
}
1

Déclaration de l'emballage

2

Paquet "testing" importé, utilisé plus tard dans t.Errorf

3

Notre méthode de test, qui doit commencer par Test et avoir un argument *testing.T

4

La structure représentant "USD 5." Dollar n'existe pas encore

5

Méthode testée : Times- qui n'existe pas encore.

6

Comparer la valeur réelle à la valeur attendue

7

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 function 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'); 1

let fiver = new Dollar(5); 2
let tenner = fiver.times(2); 3
assert.strictEqual(tenner.amount, 10); 4
1

Importer le paquet assert, nécessaire pour l'assertion plus tard

2

L'objet représentant "USD 5." Dollar n'existe pas encore

3

Méthode testée : times- qui n'existe pas encore.

4

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 1

class TestMoney(unittest.TestCase): 2
  def testMultiplication(self): 3
    fiver = Dollar(5) 4
    tenner = fiver.times(2) 5
    self.assertEqual(10, tenner.amount) 6

if __name__ == '__main__': 7
    unittest.main()
1

Importation du paquet unittest, nécessaire pour la superclasse TestCase.

2

Notre classe de test, qui doit sous-classer la classe unittest.TestCase.

3

Le nom de notre méthode doit commencer par test pour être considéré comme une méthode de test.

4

L'objet représentant "USD 5." Dollar n'existe pas encore.

5

Méthode testée : times- qui n'existe pas encore.

6

Comparer la valeur réelle à la valeur attendue dans une déclaration assertEqual.

7

L'idiome main garantit que cette classe peut être exécutée en tant que script.

Python exige importing le paquetage unittest, la création d'une classe qui sous-classe TestCase, et defining 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 !

Astuce

python3 file.py -v exécute le code Python dans file.py et produit une sortie verbeuse. Nous utilisons cette commande pour effectuer nos tests.

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 :

  1. 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.

  2. 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 function 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.00s)
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') 1
1

Ce message provient de Node.js v16 ; la v14 produit un message d'erreur légèrement différent.

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) { 1
        this.amount = amount; 2
    }

    times(multiplier) { 3
        return new Dollar(10); 4
    }
}
1

La fonction constructor est appelée chaque fois qu'un objet Dollar est créé.

2

Initialise la variable this.amount avec le paramètre donné.

3

La méthode times prend un paramètre.

4

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): 1
    self.amount = amount 2

  def times(self, multiplier): 3
    return Dollar(10) 4
1

La fonction __init__ est appelée chaque fois qu'un objet Dollar est créé.

2

Initialise la variable self.amount avec le paramètre donné.

3

La méthode times prend un paramètre.

4

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.000s

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 :

  1. 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é.

  2. 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 écrit 10 dans notre test et dans notre code. Dollar code. Nous devrions nous rendre compte que le 10 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}
}
Astuce

N'oublie pas d'exécuter go fmt ./... périodiquement pour corriger tout problème de formatage dans le code.

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 . 1
git commit -m "feat: first green test" 2
1

Ajoute tous les fichiers, y compris toutes les modifications qu'ils contiennent, à l'index Git.

2

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 1
 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
1

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) 1
Author: Saleem Siddiqui ...
Date:   Sun Mar 7 12:26:06 2021 -0600

    feat: first green test 2
1

Voici le premier commit, avec son hachage SHA complet.

2

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.