Introduction
Voici la partie 2 de notre série. il est nécessaire d'avoir lu la partie précédente pour suivre cet article.
Dans cet article, nous allons voir :
C'est parti
lançons les tests - premier essai.
La commande de test est crystal spec
. Cette commande va exécuter les test contenus dans tous les fichier _spec.cr
de toutes l'arborescence ./spec/**/*
.
Voici le code par défaut, qui a été généré avec le crystal init
dans le fichier ./spec/web_spec.cr
describe Web do
# TODO: Write tests
it "works" do
false.should eq(true)
end
end
describe
est un mot clé pour décrire une suite de test. Nous pouvons lui passer en argument soit une chaîne de caractère soit le nom d'un objet. Nous pouvons ecrire un autre bloc describe
dans celui existant. Cela aura pour effet de se concaténer.
Dans notre cas, l'objet Web
n'existe pas. la commande crystal spec
va nous répondre une erreur.
it
est le bloc de test. Nous lui passons une chaîne de caractère en argument. Chaîne qui doit décrire le test. Par convention, si le test concerne une méthode de l'objet passer en describe
, nous préfixons le nom de la méthode par un #
Ensuite, nous pouvons voir que le test écrit est voué à l’échec false
ne valant jamais true
.
Corrigeons ces deux points et regardons ce qui ce passe...
Dans l'état actuel des choses, il ne se passera rien et votre crystal spec
restera bloqué. Il faudra un CTRL+C
pour sortir.
Voici ce qu'il se passe. Notre fichier ./spec/web_spec.cr
charge le fichier ./spec/spec_helper.cr
. Ce dernier charge notre code du fichier ./src/web.cr
. Or, notre code lance un serveur HTTP et attend en boucle les requêtes. C'est ce qui bloque notre test.
Deux solutions s'offre à nous pour l'instant.
Rapide : Nous sortons la commande server.listen
dans un autre fichier. Et nous lancerons ce nouveau fichier quand nous voudrons lancer le serveur.
Par configuration : Nous conditionnons le lancement de server.listen
au fait de ne pas être en environnement de test.
Dans notre cas, nous partons sur la première solution pour évoluer vers la seconde.
Testons notre configuration.
Dans le code actuel, nous avons quelques constantes au début :
TODO_FILE_PATH = File.join(Dir.current, "todo.txt")
et quelques valeurs en dur pour la configuration de notre serveur http
server.bind_tcp "0.0.0.0", 8080
et comme nous venons de le voir, nous avons aussi besoin de déterminer dans quel environnement nous sommes.
Supprimons le fichier ./spec/web_spec.cr
qui ne nous sert pas. Créons un fichier ./spec/config_spec.cr
.
Commençons l’écriture de notre test, nous avons besoin d'un objet qui va stocker notre configuration et permettre un accès dans tout notre code.
La première constante va être TODO_FILE_PATH
.
require "./spec_helper"
describe Config do
describe "#TODO_FILE_PATH" do
pending "return a path by concat current dir with 'todo.txt' do
end
end
end
le mot clé pending
permet ne pas exécuter ce test tout en gardant la structure pour plus tard.
Passons à l’écriture du test et du module
require "./spec_helper"
describe Config do
describe "#TODO_FILE_PATH" do
it "return a path by concat current dir and todo_file_name" do
Config::TODO_FILE_PATH.should eq File.join(Dir.current, "todo.txt")
end
end
end
ce qui se résout par le code suivant :
module Config
TODO_FILE_PATH = ENV.fetch("TODO_FILE_PATH", File.join(Dir.current, "todo.txt"))
end
Crystal vient avec la librairie standard ENV
qui permet de lire et de manipuler les variables d'environnement.
J'ai écris le code du module config
dans le fichier src/web.cr
. Nous allons le déplacer dans son propre fichier src/config.cr
et le charger dans son fichier d'origine.
# src/web.cr
require "http/server"
require "./config"
...
On remplace toutes les occurrences de TODO_FILE_PATH
par Config::TODO_FILE_PATH
Continuons avec les variables du serveur http et l'environnement.
Le test
require "./spec_helper"
describe Config do
describe "#TODO_FILE_PATH" do
it "return a path by concat current dir and todo_file_name" do
Config::TODO_FILE_PATH.should eq File.join(Dir.current, "todo.txt")
end
end
describe "#environment" do
it "return development as default environment" do
ENV.delete("GTD_ENVIRONMENT")
Config.environment.should eq "development"
end
it "return passed value as environment" do
ENV["GTD_ENVIRONMENT"]= "test"
Config.environment.should eq "test"
ENV.delete("GTD_ENVIRONMENT")
end
end
describe "#PORT" do
it "return default port" do
Config::PORT.should eq 3000
end
end
describe "#BIND_IP" do
it "return default binding" do
Config::BIND_IP.should eq "0.0.0.0"
end
end
end
le module
module Config
TODO_FILE_PATH = ENV.fetch("TODO_FILE_PATH", File.join(Dir.current, "todo.txt"))
PORT = ENV.fetch("PORT", "3000").to_i
BIND_IP = ENV.fetch("BIND_IP", "0.0.0.0")
def self.environment
ENV.fetch("GTD_ENVIRONMENT", "development")
end
end
tous les tests sont passant.
Lancement du serveur sauf pour les tests.
Revenons à notre deuxième solution de gestion du lancement du serveur maintenant que nous avons le module de configuration.
Supprimons le fichier créer pour la première solution.
Puis, à la fin du fichier src/web.cr
ajoutons notre condition de lancement.
server.bind_tcp Config::BIND_IP, Config::PORT
if Config.environment != "test"
server.listen
end
Si nous lançons notre code avec crystal src/web.cr
, nous avons bien notre application disponible via http://localhost:3000/
si je lance les tests avec la commande GTD_ENVIRONMENT=test crystal spec
, les tests se lancent sans le serveur.
Il peut être lassant d'ajouter la variable d'environnement à chaque lancement. Pour ma part, j'ai ajouté cette variable dans le fichier ./spec/spec_helper.cr
comme suit :
require "spec"
ENV["GTD_ENVIRONMENT"]="test"
require "../src/web"
Conclusion
Nous avons vu les bases de Spec, la manipulation de l'environnement via ENV et la forme d'objet Module.
Dans le prochain article, nous regrouperons les méthodes liées au dépôt de tâches au sein d'une classe
Top comments (0)