Tests unitaires avec Capcode
S'il y a bien un aspect, lors de la mise en place d'une application, qui obsède les développeurs, ce sont bien les tests. Et à juste titre. Au point que nous trouvons aujourd'hui de plus en plus d'équipes qui travaillent en TDD, contre toutes les lois qui nous ont été enseignées lors de nos études. L'écriture de tests unitaires peut paraitre rébarbative, cela n'est reste pas moins une très bonne habitude à prendre pour éviter par la suite de longues heures à rechercher les bugs les plus vicieux que nous avons cachés dans notre code. Je vous propose donc de voir comment faire des tests unitaires avec Capcode.
Comme vous le savez, Capcode est basé sur Rack et il existe pour ce dernier le projet Rack::Test, idéal pour ce que nous aider dans ce travail.
Partons d'un exemple minimaliste :
1 # sample.rb
2 require 'rubygems'
3 require 'capcode'
4
5 module Capcode
6 class Index < Route '/'
7 def get
8 render "Hello World"
9 end
10 end
11
12 class Redir < Route '/r'
13 def get
14 redirect( Index )
15 end
16 end
17 end
Vous remarquerez que je n'ai volontairement pas mis l'appel à Capcode.run dans ce code. Ceci pour deux raisons. J'ai pris l'habitude de déporter cet appel dans un fichier à part ce qui me permet de créer en parallèle un fichier de configuration rackup pour le déploiement avec Passenger. De plus, Rack::Test sert à tester des applications basées sur Rack et implique d'avoir accès directement à l'application, celle-là même que vous passez à votre handler.
Rack::Test fonctionne avec Test::Unit, nous allons dons créer, pour chaque ensemble de tests, une classe héritant de Test::Unit::TestCase et nous inclurons dans cette classe le module Rack::Test::Methods :
1 # test_sample.rb
2 require 'rubygems'
3 require 'test/unit'
4 require 'rack/test'
5
6 class HomepageTest < Test::Unit::TestCase
7 include Rack::Test::Methods
8
9 # ...
10
11 end
Comme je l'ai signalé, l'utilisation de Rack::Test implique que nous ayons accès à l'application. Nous allons donc la charger et faire en sorte qu'elle soit accessible dans la classe via la méthode app :
1 # test_sample.rb
2 require 'rubygems'
3 require 'test/unit'
4 require 'rack/test'
5
6 require 'sample.rb'
7 @@app = Capcode.application( )
8
9 class HomepageTest < Test::Unit::TestCase
10 include Rack::Test::Methods
11
12 def app
13 @@app
14 end
15
16 # ...
17
18 end
Nous pouvons ensuite ajouter nos tests. Chaque test est décrit dans une méthode de la classe, ces méthodes pouvant contenir autant d'assertions que nécessaire :
1 # test_sample.rb
2 require 'rubygems'
3 require 'test/unit'
4 require 'rack/test'
5 require 'sample.rb'
6
7 @@app = Capcode.application( )
8
9 class HomepageTest < Test::Unit::TestCase
10 include Rack::Test::Methods
11
12 def app
13 @@app
14 end
15
16 def test_home
17 get '/'
18 assert_equal "http://example.org/", last_request.url
19 assert last_response.ok?
20 assert_equal "Hello World", last_response.body
21 end
22
23 def test_redirect
24 get '/r'
25 assert_equal "http://example.org/r", last_request.url
26 follow_redirect!
27 assert_equal "http://example.org/", last_request.url
28 assert_equal "Hello World", last_response.body
29 end
30 end
Si vous exécutez test_sample.rb vous devriez obtenir la sortie suivante :
Loaded suite /Users/greg/dev/Capcode/tests/test_sample
Started
..
Finished in 0.005839 seconds.
2 tests, 6 assertions, 0 failures, 0 errors
Pour les assertions, vous pouvez utiliser tout ce que propose Test::Unit. Pour la partie "navigation", Rack::Test vous offre les méthodes suivantes :
- get, post, put, delete et head permettent respectivement de faire des requêtes de type GET, POST, PUT, DELETE et HEAD. Chacune de ces méthodes prend en paramètre l'URI à tester, mais également les paramètres, sous forme de hachage et éventuellement des données d'environnement (également sous forme de hachage).
post '/login', {"user" => "greg", "password" => "s3cr3t"} - header permet de positionner la valeur d'une entête
header "User-Agent", "Firefox" - basic_authorize et digest_authorize permettent de donner le login et le mot de passe pour, respectivement, une authentification basic ou digest.
digest_authorize( "greg", "p4ssw0rd" ) - follow_redirect! permet de suivre la dernière redirection renvoyée.
- set_cookie et clear_cookie permettent de gérer les cookies.
- last_request correspond à la dernière requête. C'est une instance de Rack::Request.
- last_response correspond à la dernière réponse, c'est une instance de Rack::MockResponse.