- rspec-rails: rspec pour rails \o/
- rubocop: rubocop ça check la syntaxe de ton code :)
rails g rspec:install
Running via Spring preloader in process 24305
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
Pour configurer rspec avec Factory Bot, il faut éditer le fichier spec/rails_helper.rb
config.fixture_path = "#{::Rails.root}/spec/fixtures"
Mets en commentaire la ligne non commentée puis ajoute juste après la ligne suivante :
config.include FactoryBot::Syntax::Methods
Pour installer shouldamatcher, il faut également éditer le fichier rails_helper et ajouter :
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
- factory_bot_rails: Permet de créer facilement des instance de modèle en database.
- faker: Permet de générer de fausses données de test.
- nyan-cat-formatter: C’est mignon les nyan cat pendant les tests :)
- shoulda-matchers: Permet de vérifier les validations facilement dans les tests.
Les deux font la même chose et permettent de définir un “contexte” de test. Et donc de séparer nos tests pour les rendre plus lisibles.
On utilisera describe plus pour préciser des noms de classe/méthode:
describe MaClass do
describe "#new" do
…
end
end
On utilisera context pour décrire un contexte plus global:
describe MaClass do
context "when the user isn't logged in" do
…
end
end
it permet de faire un test. Il est conseillé de ne pas mettre “should” dedans. Par exemple:
it "has 3 elements" do
…
end
on notera l’utilisation de la troisième personne du singulier. Pourquoi fait on cela ? Outre le fait de rendre les tests plus proches de l’anglais, c’est parce qu’après, rspec permet de générer une “doc” de cette façon:
rspec --format doc
Ça permet de tester quelque chose. Une liste de matchers peut être trouvé ici: https://relishapp.com/rspec/rspec-expectations/v/3-7/docs/built-in-matchers
expect([1,2,3]).to include(2)
(avec exemples et utilisation de Factory)
let et son ami let! vous permettent de définir une variable qui sera utilisable dans vos tests. Les let sont scope par describe/context.
Un exemple d’utilisation:
context "this will work" do
let(:ma_var) { "yop" }
it "checks let" do
expect(ma_var).to eq("yop")
end
end
context "this will not work" do
it "checks let" do
expect(ma_var).to eq("yop")
end
end
La différence entre let et let! c’est que let est lazy et ne sera instancié que la première fois que la variable est appelée tandis que let! instancie la variable avant le test.C’est particulièrement pratique quand les let! instancie des objets en database.
before permet de définir un block qui sera exécuté avant chaque test. Par exemple:
before do
Turtle.create(name: 'silvie', color: 'green')
end
it "has one turtle in db" do
expect(Turtle.count).to eq(1)
end
Si l'instance pose problème, on peut ajouter ce bloc pour avoir le détail lors de la commande rspec
@stay_other_tenant_other_studio = FactoryBot.create(:stay).tap { |e| p e.valid?; p e.errors }
Il se déclare généralement après le describe et va permettre d'appeler une varibale dans tous les tests qui suivent.
C’est un peu comme un let ça permet de définir le “sujet” du test. On l’invoque plus tard dans son test en faisant subject
subject do
get :index
end
it "has an array of turtles" do
subject
expect(JSON.parse(response.body)).to be_a(Array)
end
Exemple de test de modèle avec utilisation d'un subject
require 'rails_helper'
RSpec.describe Auction, :type => :model do
subject { described_class.new }
it "is valid with valid attributes" do
subject.title = "Anything"
expect(subject).to be_valid
end
it "is not valid without a title" do
subject.title = nil
expect(subject).to_not be_valid
end
it "is not valid without a description"
subject.description = nil
expect(subject).to_not be_valid
end
end
Parfois il est nécessaire de combiner un subject avec un let.
Voici un exemple :
let(:seller) { User.new(:email => "jane@doe.com", :password => "pw1234",
:password_confirmation => "pw1234") }
subject {
described_class.new(title: "Anything", description: "Lorem ipsum",
start_date: DateTime.now, end_date: DateTime.now + 1.week,
seller: seller)
}
La plupart du temps, subject est utilisé pour renvoyer une instance de classe.
FactoryBot est une gem pour nous aider à définir des factories qui permettent de générer des objets en DB dans nos tests.
On l’installe en ajoutant dans spec/rails_helper dans le block de configuration:
config.include FactoryBot::Syntax::Methods
Les factories sont définies dans spec/factories et ressemblent à:
FactoryBot.define do
factory :turtle do
name { Faker::OnePiece.character }
color { %w(blue green pink yellow).sample }
end
end
On les invoque plus tard dans nos tests de cette façon:
# créer une tortue en db.
create(:turtle)
# créer une tortue en db en spécifiant certains de ses attributs
create(:turtle, name: 'Léonardo')
# créer plusieurs instances (12 ici)
create_list(:turtle, 12)
Permet de tester les validation des modèles (entre autre).
On l’installe en ajoutant à la fin du spec/rails_helper
Shoulda::Matchers.configure do |config|
config.integrate do |with|
# Choose a test framework:
with.test_framework :rspec
with.library :rails
end
end
Par exemple si le modèle défini une validation de présence:
validates :name, presence: true on la test de cette façon:
it { should validate_presence_of(:name) }
La liste des helpers de tests se trouve ici: https://github.com/thoughtbot/shoulda-matchers
require 'rails_helper'
# RSpec.describe Product, type: :model do
# end
RSpec.describe Product, type: :model do
describe 'Database' do
it { is_expected.to have_db_column(:id).of_type(:integer)}
it { is_expected.to have_db_column(:description).of_type(:text)}
it { is_expected.to have_db_column(:image).of_type(:string)}
it { is_expected.to have_db_column(:price).of_type(:float)}
it { is_expected.to have_db_column(:title).of_type(:string)}
it { is_expected.to have_db_column(:updated_at).of_type(:datetime)}
it { is_expected.to have_db_column(:updated_at).of_type(:datetime)}
it {is_expected.to have_db_column(:category_id).of_type(:integer)}
end
end
describe 'Product attributes' do
let(:product) { build(:product) }
context 'Price' do
it { expect(product.price).to be > 0 }
end
context 'Image https' do
it { expect(product.image).to match(/https:/)}
end
end
## Shoulda Matcher : is_expected.to ==> should
describe 'Product Association' do
it { is_expected.to belong_to(:category) }
it { is_expected.to have_many(:selections) }
it { should have_many(:carts) }
it { should have_many(:order_products) }
it { should have_many(:orders) }
end
## Shoulda Matcher : is_expected.to ==> should
describe 'Product Validates' do
it { is_expected.to validate_presence_of(:price) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:description) }
it { should validate_presence_of(:image ) }
it { should validate_presence_of(:category_id ) }
end
On peut également tester les validates en créant une instance et en passant successivement chaque attribut obligatoire à nil.
Exemple :
########## ATTRIBUTES ############
describe 'Product attributes' do
let(:product) { build(:product) }
it "is valid without any errors" do
expect(product.errors).to be_empty
end
it "is valid with valid attributes" do
expect(product).to be_valid
end
context "product should not be valid" do
it "is not valid with missing all attributes" do
expect(Product.new).to_not be_valid
end
it "is not valid with missing price attributes" do
product.price = nil
expect(product).to_not be_valid
end
it "is not valid with missing descritpion attributes" do
product.description = nil
expect(product).to_not be_valid
end
it "is not valid with missing titile attributes" do
product.title = nil
expect(product).to_not be_valid
end
end
##Cypress
$npx cypress open
Permet d'ouvrir Cypress en mode interactif
$npx cypress run
Permet d'ouvrir cypress dans le terminal sans visuel (headless mode)
https://semaphoreci.com/community/tutorials/how-to-test-rails-models-with-rspec
https://kolosek.com/rspec-controller-test/
https://kolosek.com/rspec-let-vs-before/
http://www.betterspecs.org/#subject
https://devhints.io/factory_bot
https://relishapp.com/rspec/rspec-expectations/v/3-7/docs/built-in-matchers
https://devhints.io/rspec-rails
https://docs.cypress.io/guides/getting-started/installing-cypress.html#Installing