Simple Enough Blog logo
  • Home 
  • Projets 
  • Tags 

  •  Langage
    • English
    • Français
  1.   Blogs
  1. Accueil
  2. Blogs
  3. Utiliser les constantes en TDD avec Go

Utiliser les constantes en TDD avec Go

Posté le 23 décembre 2025 • 7 min de lecture • 1 310 mots
Golang   Tdd   Architecture   Tests   Devops   Thibault  
Golang   Tdd   Architecture   Tests   Devops   Thibault  
Partager via
Simple Enough Blog
Lien copié dans le presse-papier

Comment utiliser correctement les constantes en Test-Driven Development avec Go.

Sur cette page
Bonnes pratiques, pièges et signaux de design   I. Rappel : le rôle du TDD dans le design   II. Ce que signifie réellement const en Go   Impact en TDD   III. Deux grandes familles de constantes   1. Constantes de domaine (métier)   2. Constantes techniques   IV. Le piège classique : dupliquer les constantes dans les tests   Anti-pattern   Bon pattern TDD   V. Une heuristique simple (et très fiable)   VI. Constantes vs paramètres : le vrai signal de design   Pattern Go recommandé : Default + Options   VII. Constantes et erreurs : un pattern clé en Go   1. Erreurs sentinelles (recommandé)   2. Erreurs typées (plus avancé)   VIII. Cas critique : timeouts et TDD   Exemple d’un mauvais design   Exemple d’un design TDD-friendly   IX. Table-driven tests et constantes   X. Exemple complet : TDD propre avec constantes et options   Spécification   XI. Règles d’or (à garder sous la main)   Conclusion   🔗 Ressource utile  
Utiliser les constantes en TDD avec Go
Photo par Thibaut Deheurles

Bonnes pratiques, pièges et signaux de design  

Le Test-Driven Development (TDD) ne sert pas uniquement à écrire des tests.
Il sert avant tout à concevoir du logiciel.

En Go, l’usage des constantes est souvent mal compris en TDD :

  • faut-il les exposer ?
  • les tester ?
  • les injecter ?
  • les éviter ?

Cet article propose une approche pragmatique et idiomatique Go, issue de l’expérience terrain, pour comprendre quand une constante est un bon design… et quand elle cache un problème.


I. Rappel : le rôle du TDD dans le design  

cycle TDD

Le cycle TDD est simple :

  1. Red – écrire un test qui échoue
  2. Green – écrire le minimum pour le faire passer
  3. Refactor – améliorer le design sans changer le comportement

Les constantes apparaissent surtout au moment du refactor.
Elles sont rarement une optimisation : ce sont des décisions de design.


II. Ce que signifie réellement const en Go  

En Go, une constante est :

  • évaluée à la compilation
  • immuable
  • parfois non typée (jusqu’à son usage)
const MaxRetries = 3        // non typée
const MaxRetriesInt int = 3 // typée

Impact en TDD  

  • Les constantes non typées sont souvent plus souples dans les tests
  • Les constantes typées peuvent sécuriser une API publique
  • Une constante ne peut pas dépendre du runtime (time.Now, variables d’environnement, etc.)

Si une valeur dépend de l’environnement ou du temps, ce n’est pas une constante.


III. Deux grandes familles de constantes  

1. Constantes de domaine (métier)  

Ce sont des invariants fonctionnels :

  • seuils
  • limites
  • règles métier explicites
const MaxLoginAttempts = 5

Caractéristiques :

  • souvent exportées
  • documentées
  • utilisées par le code ET les tests

En TDD, ce sont les constantes les plus importantes.

2. Constantes techniques  

Exemples :

  • taille de buffer
  • chunk size
  • limite interne de batch
  • valeur de performance
const bufferSize = 32 * 1024

Caractéristiques :

  • souvent privées
  • susceptibles de changer
  • pas directement testées

Les tests doivent vérifier le comportement, pas la valeur.


IV. Le piège classique : dupliquer les constantes dans les tests  

Anti-pattern  

// production
const MaxRetries = 3

// test
const maxRetries = 3

Dupliquer une constante entre le code de production et les tests est une pratique dangereuse, car elle introduit deux sources de vérité distinctes. Dans ce cas, un changement de règle métier peut laisser les tests au vert sans que le comportement réel du système soit correctement validé. Le test cesse alors de vérifier la règle métier et se contente de confirmer une implémentation figée

Bon pattern TDD  

Le test doit utiliser la constante de production :

if !ShouldStop(MaxRetries + 1) {
    t.Fatal("expected stop")
}

À l’inverse, un bon design en TDD consiste à utiliser directement la constante de production dans les tests. De cette manière, le test exprime explicitement la règle métier attendue, par exemple en vérifiant qu’un dépassement de la limite déclenche bien l’arrêt du traitement. Le résultat est un test plus robuste, plus expressif, et aligné avec le langage du domaine : il valide une intention fonctionnelle, et non une simple valeur numérique.


V. Une heuristique simple (et très fiable)  

Exemple en test :

for i := 0; i < 5; i++ {
    ...
}

Lorsqu’une valeur mérite un nom explicite dans le code, c’est qu’elle porte un sens particulier dans le design. Dans ce cas, elle doit être soit une constante métier, soit un paramètre configurable. Prenons l’exemple d’une boucle de test utilisant la valeur 5 comme borne : avant de l’extraire dans une constante, il est essentiel de se demander si ce nombre représente une règle métier réelle. Si c’est le cas, alors cette valeur doit devenir une constante clairement nommée, capable d’exprimer l’intention fonctionnelle du code. En revanche, si ce nombre n’a aucune signification métier, il est préférable de le laisser inline afin d’éviter un bruit conceptuel inutile. Enfin, si cette valeur est amenée à changer fréquemment selon les contextes ou les scénarios de test, elle ne doit pas être figée : elle doit être modélisée comme un paramètre ou une option, afin de préserver la flexibilité du design.

En résumé :

  • 5 est-il une règle métier ?
    • oui → constante
    • non → laisse inline

Si cette valeur change souvent :

  • ce n’est pas une constante
  • c’est un paramètre (ou une option)

VI. Constantes vs paramètres : le vrai signal de design  

Une constante qui change souvent est un mauvais signe

const Timeout = 2 * time.Second

Si tu la modifies régulièrement :

  • tests instables
  • refactors pénibles
  • design rigide

Résultat : Elle devrait être injectée, pas figée.

Pattern Go recommandé : Default + Options  

const DefaultRetryLimit = 3

type Retrier struct {
    limit int
}

type Option func(*Retrier)

func WithLimit(n int) Option {
    return func(r *Retrier) { r.limit = n }
}

func NewRetrier(opts ...Option) *Retrier {
    r := &Retrier{limit: DefaultRetryLimit}
    for _, opt := range opts {
        opt(r)
    }
    return r
}

Avantages en TDD :

  • le default est testable
  • les cas limites sont faciles à couvrir
  • le design reste flexible

VII. Constantes et erreurs : un pattern clé en Go  

1. Erreurs sentinelles (recommandé)  

var ErrTooManyRetries = errors.New("too many retries")

Test:

if !errors.Is(err, ErrTooManyRetries) {
t.Fatalf("unexpected error")
}

Résultat : Stable, lisible, refactor-friendly.

2. Erreurs typées (plus avancé)  

type RetryError struct {
    Limit int
}

func (e RetryError) Error() string {
    return "too many retries"
}

Test

var re RetryError
if errors.As(err, &re) {
    if re.Limit != DefaultRetryLimit {
        t.Fatal("unexpected limit")
    }
}

Résultat : Les constantes deviennent des invariants observables.


VIII. Cas critique : timeouts et TDD  

Les timeouts sont l’ennemi du TDD s’ils sont codés en dur.

Exemple d’un mauvais design  

const Timeout = 2 * time.Second

ctx, cancel := context.WithTimeout(ctx, Timeout)

Valeurs sans sens métier :

{"spaces", "   ", false}

Conséquences :

  • tests lents
  • usage de time.Sleep
  • flakiness

Exemple d’un design TDD-friendly  

type Client struct {
    timeout time.Duration
}

func NewClient(timeout time.Duration) *Client {
    return &Client{timeout: timeout}
}

Avec une valeur par défaut :

const DefaultTimeout = 2 * time.Second

func NewDefaultClient() *Client {
    return NewClient(DefaultTimeout)
}

Tests :

  • timeout court
  • timeout nul
  • pas de sleep

IX. Table-driven tests et constantes  

Utilise des constantes pour :

  • les cas symboliques
  • les bornes
  • les invariants
const (
    ValidUserID   = "user-123"
    InvalidUserID = ""
)

Valeurs sans sens métier :

{"spaces", "   ", false}

Résultat : Lisibilité maximale, bruit minimal.


X. Exemple complet : TDD propre avec constantes et options  

Spécification  

  • rate limiter
  • default : 100 req/min
  • configurable
  • erreur explicite au dépassement
const DefaultLimitPerMinute = 100

var ErrRateLimited = errors.New("rate limited")

type Limiter struct {
    limit int
    used  int
}

func WithLimit(n int) Option {
    return func(l *Limiter) { l.limit = n }
}

func NewLimiter(opts ...Option) *Limiter {
    l := &Limiter{limit: DefaultLimitPerMinute}
    for _, opt := range opts {
        opt(l)
    }
    return l
}

Tests :

  • un test sur le default
  • un test sur la variation
  • aucune duplication de valeur

Résultat : Design robuste, testable, idiomatique Go.


XI. Règles d’or (à garder sous la main)  

  • Une constante est une décision de design
  • Les constantes métier doivent être visibles par les tests
  • Ne duplique jamais une constante de production dans un test
  • Si ça change souvent → paramètre ou option
  • Timeouts et backoff → injection, jamais figés
  • Les tests doivent parler le langage du domaine, pas celui des chiffres

Conclusion  

En TDD, les constantes ne servent pas à “faire propre”.
Elles servent à rendre le design explicite.

En Go, bien utilisées, elles deviennent :

  • des invariants documentés
  • des points d’ancrage pour les tests
  • des signaux clairs d’architecture

Et quand une constante commence à poser problème,

le TDD te donne le signal le plus précieux : ton design demande à évoluer.


🔗 Ressource utile  

  • Dave Farley – Modern TDD & Continuous Delivery (YouTube)
    Conférences et retours d’expérience très concrets sur le TDD appliqué à des systèmes réels.
  • Effective Go – Constants & Design
    La documentation officielle Go, essentielle pour comprendre les choix idiomatiques du langage.
    https://go.dev/doc/effective_go
 « Ça rame », « ça lag », « ça bug » : mais au final qu'est ce que ça veut vraiement dire ?
Interfaces, Fonctions et Modules en Go : Structurer son code pour le TDD sans le complexifier 
  • Bonnes pratiques, pièges et signaux de design  
  • I. Rappel : le rôle du TDD dans le design  
  • II. Ce que signifie réellement const en Go  
  • III. Deux grandes familles de constantes  
  • IV. Le piège classique : dupliquer les constantes dans les tests  
  • V. Une heuristique simple (et très fiable)  
  • VI. Constantes vs paramètres : le vrai signal de design  
  • VII. Constantes et erreurs : un pattern clé en Go  
  • VIII. Cas critique : timeouts et TDD  
  • IX. Table-driven tests et constantes  
  • X. Exemple complet : TDD propre avec constantes et options  
  • XI. Règles d’or (à garder sous la main)  
  • Conclusion  
  • 🔗 Ressource utile  
Suivez-nous

Nous travaillons avec vous !

   
Copyright © 2026 Simple Enough Blog Tous droits réservés. | Propulsé par Hinode.
Simple Enough Blog
Code copié dans le presse-papier