Le blog de la CT2C

Tester son application rails

Par Arpsara , publié le 17 Août 2016

Petite définition


Tout d'abord, que veut dire "tester" son application rails? On est bien d'accord, ce n'est pas cliquer sur tous les boutons et s'assurer que dans tous les cas possibles et imaginables, votre application va marcher. Tester en rails, cela veut dire écrire des petits bouts de code pour s'assurer que votre code final va bien faire ce que vous souhaitez qu'il fasse.

Un exemple tout simple:
Supposons que vous ayez un utilisateur qui a un prénom (John) et un nom (Doe), enregistrés dans 2 attributs différents. Vous souhaitez créer une fonction nommée "fullname" qui permettrait de récupérer le prénom et le nom de l'utilisateur concaténés (soit "John Doe").

Un test consisterait à écrire ceci:
# On crée un utilisateur John Doe
let(:user) { create(:user, firstname: "John", lastname: 'Doe') }

# Test de la fonction fullname
describe '#fullname' do
it 'should render user firstname and lastname' do
expect(user.fullname).to eq "John Doe"
end
end

Cela a l'air plutôt facile et même futile. Alors à quoi cela sert-il de tester son application? On a ici évidemment pris une exemple simple, mais déjà, on peut un peu le complexifier en se posant la question: que se passe-t-il si le nom et le prénom ne sont pas obligatoires et que l'utilisateur les a laissés blancs? On pourrait par exemple vouloir afficher l'email de l'utilisateur si tel était le cas. La fonction peut ainsi se complexifier au fil du temps, et avoir des tests permettent de s'assurer que la fonction (et donc votre code) marche toujours. (Un exemple tout bête mais qui peut arriver: vous n'avez plus besoin de l'attribut lastname et décidez de le supprimer de la base de donnée. Vous vous remercierez d'avoir fait un test sur cette fonction fullname, car il vous aura permis de détecter qu'elle ne marche plus et agir en conséquence. :) )

Comment et avec quoi tester son application rails?

Il y a différentes syntaxes possibles en fonction de la gem utilisée. Pour notre part, nous allons utiliser RSpec, car la syntaxe est assez intuitive et facile à comprendre: parfait pour commencer. :)
A cela, on ajoutera factory_girl_rails pour créer facilement des données et shoulda-matchers pour les associations.

# Gemfile
group :development, :test do
gem 'rspec-rails' #, '~> 3.0.0'
end
group :test do
gem 'shoulda-matchers', '~> 2.5.0'
gem "factory_girl_rails", "~> 4.0", :require => false
end

Aussi bizarre que cela puisse paraître, il est d'usage d'écrire les tests avant d'écrire le code. C'est le Test-Driven-Development (TDD). Cela peut paraître curieux et ardu de se lancer directement dans ce type de démarche, mais à force d'écrire des tests, vous verrez que ce n'est pas tellement long et si compliqué.

Exemple de test d'un model


# in app/models/users/user.rb
class User < ActiveRecord::Base
has_many :notifications

def fullname
"#{self.firstname} #{self.lastname}"
end

def self.visible
self.where(deleted: false)
end
end

Tout d'abord, comme on teste avec Rspec, les fichiers de tests vont se trouver dans le dossier spec. La structure des dossiers sera exactement la même que celle dans app/, c'est à dire que le fichier app/models/user.rb sera testé par le fichier spec/models/user_spec.rb, le fichier app/controllers/users_controller.rb sera testé par le fichier spec/controllers/users_controller_spec.rb, etc.

Un fichier test "de base" pour un model ressemble à ceci:

# in spec/models/users/user_spec.rb
require 'rails_helper'

RSpec.describe User, :type => :model do
## Les tests se trouveront ici
end

Il y a deux syntaxes possibles pour écrire un test:

it { should_do_something_here } # cas où l'on a qu'une seule ligne de test

# ou encore

describe '#nomDeMaFonction' do
it 'should do description de ce que la fonction doit faire' do
# mon test ici
# et là, je peux écrire sur plusieurs lignes
end
it 'should do description de ce que la fonction doit faire 2' do
# mon deuxième test ici
# et là, je peux écrire sur plusieurs lignes
end
end

Maintenant, revenons-en à notre exemple.
Pour les associations, rien de plus simple grâce à la gem shoulda-matchers.

  it { should have_many :notifications }  # permet de tester has_many :notifications

Il y a bien sûr des tests pour tous les types d'association, il suffit de regarder la documentation (ou à la limite presque de faire de l'anglais) pour les retrouver. :)

Pour les méthodes, nous avons besoin de créer des utilisateurs afin de vérifier que la fonction est viable. Pour cela, on peut utiliser factory girl. Cela nous permet de créer un utilisateur de base.

# in factories/users.rb
FactoryGirl.define do
factory :user do
firstname "John"
lastname "Doe"
visible true
end
end

Pour créer l'utilisateur avec tous ces attributs, il me suffira d'appeler:

user = create(:user)

Et user.firstname me donnera ainsi John, user.lastname sera égal à Doe, etc.
Pratique, non? Et ça l'est d'autant plus que l'on peut écraser les attributs comme on va le faire ci-dessous avec "Jane"

Par exemple
  
let(:user) { create(:user) }
let(:deleted_user) { create(:user, firstname: "Jane", visible: false) }

let permet de créer une variable commune à différents tests. Ils se mettent directement après le "RSpec.describe User, :type => :model do" Un avantage appréciable par rapport à @user, c'est qu'il n'y a pas besoin de mettre le @, ce qui rend les tests plus lisibles. Bref.

Une fois ces deux utilisateurs crées, ajoutons nos tests. Vous remarquerez qu'on ajoute un "#" devant le nom de la fonction testée pour indiquer qu'il s'agit d'une méthode d'instance (par exemple #fullname) et un "." pour indiquer qu'il s'agit d'une méthode de classe (ici .visible).

  # in spec/models/users/user_spec.rb

describe '#fullname' do
it 'should render user firstname and lastname' do
expect(user.fullname).to eq "John Doe"
end
end

describe '.visible' do
it 'should get none deleted users' do
expect(User.visible).to eq [user]
end
end

Et hop, il n'y a plus qu'à lancer "rspec" dans la console pour lancer la suite de tests. :) Si vous commentez votre model, vous devriez voir les tests échouer. Si vous décommentez, magie (ou pas), ça devrait marcher! :)

Il y a de nombreuses autres conditions autres possibles que "eq" (equal). On peut avoir:

expect( 2 - 1 ).to eq 1
expect( 2 - 1 ).not_to eq 2
expect( 2 - 1 ).to be > 0
expect( 2 - 1 ).not_to be nil
etc.

Allez donc voir la documentation de rspec sur les matchers pour en découvrir plein d'autres.


Index -- --

  • Aucun commentaire - Soyez le premier !

Insérez votre commentaire
  1. Min: 50 caractères, Max: 800. Actuellement: 0 caractères

  2. ne pas remplir