Skip to content

Tests ​

L'API a environ 460 tests. Le pipeline Jenkins les lance avant chaque déploiement, contre une base PostgreSQL jetable : une image qui échoue ses tests n'arrive jamais en production (voir Pipelines CI/CD). Lance-les aussi avant d'ouvrir une PR.

bash
mix test                                         # tous
mix test test/timemanager/clocks_test.exs        # un fichier
mix test test/timemanager/clocks_test.exs:42     # un seul test (sa ligne)
mix test --failed                                # ceux qui ont échoué la dernière fois

Deux sortes de tests ​

DossierCe qu'on testeComment
test/timemanager/ (15 fichiers)le métier : une règle, une fonction d'un contexteen appelant directement Clocks.clock/2, Corrections.approve/3…
test/timemanager_web/ (24 fichiers)les routes : statut, JSON, droits, erreurspar de vraies requêtes HTTP : post(conn, ~p"/api/clocks/5", …)

Une règle se teste au plus près de là où elle vit : une règle du métier dans test/timemanager/, et le comportement HTTP (le 403, le format de la réponse) dans test/timemanager_web/.

Les outils ​

Une base propre pour chaque test ​

Chaque test tourne dans une transaction annulée à la fin (le sandbox d'Ecto, test/support/data_case.ex). Ce qu'un test crée disparaît ensuite : aucun test ne dépend d'un autre, ni de l'ordre dans lequel ils tournent.

Fabriquer des données : les fixtures ​

test/support/fixtures/ contient des fonctions qui créent ce dont un test a besoin, en une ligne :

FonctionCrée
active_user_fixture(attrs, rôle)un compte actif ("employee" par défaut) avec le mot de passe valid_password()
user_fixture(attrs)un compte invité
team_with_members_fixture(manager, membres)une équipe, son manager et ses membres
time_tracking_fixture(attrs, personne, manager)une journée de travail
correction_request_fixture(personne, attrs)une demande de correction
unique_email()une adresse jamais utilisée

Se connecter : log_in/2 ​

elixir
conn = log_in(conn, user)   # test/support/conn_case.ex
json_response(get(conn, ~p"/api/auth/me"), 200)

Pour tester l'application mobile, on se connecte avec "client": "mobile" et on envoie l'en-tête Authorization: Bearer … (voir mobile_session_test.exs).

Lire une erreur : errors_on/1 ​

elixir
assert {:error, changeset} = Accounts.create_user(%{})
assert %{email: ["can't be blank"]} = errors_on(changeset)

Vérifier un e-mail ​

En test, aucun e-mail ne part. assert_email_sent/1 vérifie qu'il aurait été envoyé, et permet de lire son contenu (par exemple pour récupérer le lien d'un e-mail de réinitialisation).

La date figée ​

Certaines règles dépendent du jour : « une correction porte sur les 4 dernières semaines ». Un test écrit avec une date fixe (le 25 septembre) passerait en octobre et échouerait en novembre. Les tests utilisent donc un « aujourd'hui » figé au 12 octobre 2026 (:fixed_today dans config/test.exs).

Les règles qui dépendent de l'heure (une heure envoyée par le téléphone : pas dans le futur, au plus 72 h) se testent, elles, avec des heures relatives à maintenant : ago(180) = il y a 3 heures.

Les tests qui gardent la documentation honnête ​

test/timemanager_web/api_docs_test.exs vérifie que :

  1. chaque route du routeur est décrite dans Swagger ;
  2. les vraies réponses (connexion, pointage, journées, demandes, erreurs…) respectent les schémas de la documentation.

Une documentation qui ment fait donc échouer les tests.

Écrire un bon test ​

  • Un test = un comportement, et son nom le raconte : "a clock-out whose answer was lost doesn't become a clock-in".
  • Vérifier qu'il peut échouer : enlève la règle testée, le test doit devenir rouge. Un test qui passe toujours ne protège rien.
  • Tester les refus autant que les succès : le 403, le 409, le code d'erreur.
  • Pas de date fixe pour une règle qui dépend du jour : la date figée, ou une date relative.