Teste sur le parcours Elixir

Apprends à tester tes exercices Elixir sur Exercism


Depuis le terminal, place-toi dans le répertoire de base de l'exercice, puis exécute les tests avec :

$ mix test

Cette commande exécute le fichier de test situé dans le sous-dossier test, un fichier qui se termine par _test.exs

Tests en attente

Dans les suites de tests des exercices d'entraînement, tous les tests sauf le premier sont marqués pour être ignorés.

Une fois qu'un test passe, tu peux réactiver le suivant en commentant le @tag :pending correspondant avec un symbole #.

Par exemple :

# @tag :pending
test "shouting" do
  assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end

Si tu veux exécuter tous les tests d'un coup, tu peux inclure tous les tests ignorés grâce à l'option --include de la commande mix test :

$ mix test --include pending

Ou bien tu peux activer tous les tests en commentant la ligne ExUnit.configure de la suite de tests.

# ExUnit.configure exclude: :pending, trace: true

Autres fonctionnalités de test d'Elixir

ExUnit et mix test proposent pas mal de méthodes pour regrouper, étiqueter et exécuter des tests, ainsi que diverses façons de contrôler l'exécution des tests ; une bonne partie est résumée ci-dessous.

Méthodes pour exécuter des tests précis

Documentation :

Exécute les tests d'un fichier précis

Tous les tests d'un même fichier peuvent être exécutés avec mix test en précisant le fichier :

$ mix test test/<FILE>.exs

REMARQUE : tagging peut influencer les tests qui sont réellement exécutés avec cette méthode.

Exécute un test isolé

Un test isolé peut être exécuté en indiquant son numéro de ligne dans le fichier :

$ mix test test/<FILE>.exs:LINENUM

Plusieurs tests peuvent être exécutés en donnant plusieurs numéros de ligne séparés par :.

Par exemple, pour un fichier dont le contenu est le suivant, avec les numéros de ligne :

test "Test 1" do           # 1
  # test implementation    # 2-6
end                        # 7
                           # 8
test "Test 2" do           # 9
  # test implementation    # 10-21
end                        # 22
                           # 23
test "Test 3" do           # 24
  # test implementation    # 25-35
end                        # 36

On peut exécuter le 1er et le 3e test avec :

$ mix test test/FILE.exs:1:24

REMARQUE : lorsqu'on indique des tests par leur numéro de ligne, tagging est ignoré.

Exécute des groupes de tests

Les tests peuvent être regroupés avec describe :

describe "short test group description" do
  test "test description" do
    # test implementation
  end

  test "another test description" do
    # test implementation
  end
end

Tous les tests d'un groupe peuvent être exécutés en indiquant le numéro de ligne du groupe dans le fichier, exactement comme pour un test isolé.

Documentation :

Autres options utiles de mix test

  • --include et --exclude : exécutent ou non certains tests selon leurs @tag
  • --failed : n'exécute que les tests qui ont échoué lors de leur dernière exécution
  • --max-failures : la suite arrête d'évaluer les tests lorsque ce nombre d'échecs est atteint
  • --seed : initialise le générateur de nombres aléatoires utilisé pour mélanger l'ordre des tests --seed 0 désactive ce mélange, si bien que les tests d'un même fichier sont toujours exécutés dans l'ordre dans lequel ils ont été définis
  • --stale : n'exécute que les tests qui font référence à des modules modifiés depuis la dernière fois que les tests ont été lancés avec --stale
  • --only task_id:1 : ou avec un autre numéro ; sur les exercices d'apprentissage, n'exécute que les tests associés à la tâche indiquée

Documentation :

Typespecs et Dialyzer (DIscrepancy AnalYZer for ERlang programs)

Les exercices Elixir contiennent un fichier d'implémentation squelette dans le sous-dossier lib. Ce fichier décrit le module et les fonctions que tu es censé implémenter. Dans la plupart des exercices, tu trouveras des typespecs au-dessus de la déclaration de fonction. Elles commencent par l'attribut @spec et suivent généralement le format @spec function_name(type1, type2) :: return_type. En Elixir et en Erlang, elles servent de documentation et, associées à un outil appelé Dialyzer, à repérer les incohérences de types et les bugs potentiels. Pour plus d'informations, consulte la documentation sur les typespecs. Pour la documentation de Dialyzer, consulte Erlang -- dialyzer.

Tu peux aussi, si tu le souhaites, vérifier les types de ton implémentation avec Dialyzer. Cela demande quelques étapes. Pour ce faire, tu dois ajouter la dépendance Dialyxir au fichier mix.exs de ton exercice.

defp deps do
  # Add this:
  [{:dialyxir, "~> 0.4", only: [:dev]}]
end

Ensuite, utilise les tâches mix pour récupérer et compiler depuis la ligne de commande :

$ mix deps.get
...
$ mix deps.compile
...

Si c'est la première fois que tu utilises Dialyzer, tu n'as très probablement pas de fichier plt. La table de correspondance persistante (persistent lookup table, ou PLT) sert à Dialyzer pour mettre en cache des informations sur les types intégrés d'Elixir et d'Erlang. Pour créer un plt avec des valeurs par défaut raisonnables, lance :

$ mix dialyzer --plt

Enfin, tu peux le lancer avec :

$ mix dialyzer

Pense à adapter le chemin à celui des bibliothèques Elixir sur ton système. Par exemple, si tu as installé Elixir avec homebrew, tu le trouveras probablement sous /usr/local/Cellar/elixir/1.3.2.

Rappelons que lancer Dialyzer et supprimer tous les avertissements est une étape facultative pour terminer un exercice. Les avertissements de Dialyzer peuvent être difficiles à déchiffrer. Regarde par exemple les avertissements de cette implémentation vraiment ridicule de l'exercice Bob.

defmodule Bob do
  @spec hey(input :: String.t()) :: String.t()
  def hey(input) do
    1
  end

  def hey(input) do
  end
end

Cela produit les avertissements suivants.

bob.exs:2: Invalid type specification for function 'Elixir.Bob':hey/1. The success typing is (_) -> 1
bob.exs:7: The variable _input@1 can never match since previous clauses completely covered the type any()

Le premier avertissement signifie que la fonction ne renvoie pas le bon type. Le dernier indique que la seconde définition de fonction ne sera jamais atteinte, car la première définition de fonction correspond toujours.