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.
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 foisDeux sortes de tests
| Dossier | Ce qu'on teste | Comment |
|---|---|---|
test/timemanager/ (15 fichiers) | le métier : une règle, une fonction d'un contexte | en appelant directement Clocks.clock/2, Corrections.approve/3… |
test/timemanager_web/ (24 fichiers) | les routes : statut, JSON, droits, erreurs | par 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 :
| Fonction | Cré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
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
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 :
- chaque route du routeur est décrite dans Swagger ;
- 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.