rspec best friends @ tdc florianópolis 2014
Post on 19-Jan-2015
635 Views
Preview:
DESCRIPTION
TRANSCRIPT
RSpec Best Friends
Mauro quem...
RSpec Best Friends
RSpec Best Friends
maurogeorge.com.br
RSpec
sintaxe de expectativaRSpec
spec/models/pokemon_spec.rb
it 'exibe o nome e o id nacional' do pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) pokemon.nome_completo.should eq('Charizard - 6')end
spec/models/pokemon_spec.rb
it 'exibe o nome e o id nacional' do pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) expect(pokemon.nome_completo).to eq('Charizard - 6')end
spec/models/pokemon_spec.rb
it { expect(subject).to be_a(ActiveRecord::Base) }
spec/models/pokemon_spec.rb
it { should be_a(ActiveRecord::Base) }
spec/models/pokemon_spec.rb
it { is_expected.to be_a(ActiveRecord::Base) }
Somente RSpec 3
spec/spec_helper.rb
RSpec.configure do |config| # ... config.expect_with :rspec do |c| c.syntax = :expect endend
descrevendo melhor os testesRSpec
spec/models/pokemon_spec.rb
it 'exibe o nome e o id nacional' do pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) expect(pokemon.nome_completo).to eq('Charizard - 6')end
spec/models/pokemon_spec.rb
describe '#nome_completo' do it 'exibe o nome e o id nacional' do pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) expect(pokemon.nome_completo).to eq('Charizard - 6') endend
não teste apenas o happy pathRSpec
spec/models/pokemon_spec.rb
describe '#nome_completo' do it 'exibe o nome e o id nacional' do pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) expect(pokemon.nome_completo).to eq('Charizard - 6') endend
spec/models/pokemon_spec.rb
describe '#nome_completo' do it 'exibe o nome e o id nacional quando possui os valores' do pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) expect(pokemon.nome_completo).to eq('Charizard - 6') end
it 'é nil quando não possui o nome e o id nacional' do pokemon = Pokemon.new expect(pokemon.nome_completo).to be_nil endend
contextos para a melhor descriçãoRSpec
spec/models/pokemon_spec.rb
describe '#nome_completo' do context 'quando possui nome e o id nacional' do it 'exibe o nome e o id nacional' do # ... end end
context 'quando não possui o nome e o id nacional' do it 'é nil' do # ... end endend
de!nindo o sujeitoRSpec
spec/models/pokemon_spec.rb
context 'quando possui nome e o id nacional' do before do @pokemon = Pokemon.new(nome: 'Charizard', id_nacional: 6) end
it 'exibe o nome e o id nacional' do expect(@pokemon.nome_completo).to eq('Charizard - 6') endend
spec/models/pokemon_spec.rb
context 'quando possui nome e o id nacional' do let(:pokemon) do Pokemon.new(nome: 'Charizard', id_nacional: 6) end
it 'exibe o nome e o id nacional' do expect(pokemon.nome_completo).to eq('Charizard - 6') endend
spec/models/pokemon_spec.rb
context 'quando possui nome e o id nacional' do subject do Pokemon.new(nome: 'Charizard', id_nacional: 6) end
it 'exibe o nome e o id nacional' do expect(subject.nome_completo).to eq('Charizard - 6') endend
utilize sempre os matchersRSpec
spec/models/pokemon_spec.rb
it 'é nil' do expect(subject.nome_completo).to eq(nil)end
spec/models/pokemon_spec.rb
it 'é nil' do expect(subject.nome_completo).to be_nilend
não use shouldRSpec
spec/models/pokemon_spec.rb
it 'should have the name and the national_id' do expect(pokemon.full_name).to eq('Charizard - 6')end
spec/models/pokemon_spec.rb
it 'does have the name and the national_id' do expect(pokemon.full_name).to eq('Charizard - 6')end
ordem aleatória nos testesRSpec
spec/spec_helper.rb
RSpec.configure do |config| # ... config.order = "random"end
coding styleRSpec
https://github.com/mongoid/mongoid
coding styleRSpec
https://github.com/mongoid/mongoidhttps://github.com/bbatsov/ruby-style-guide
coding styleRSpec
https://github.com/mongoid/mongoidhttps://github.com/bbatsov/ruby-style-guidehttps://github.com/bbatsov/rails-style-guide
coding styleRSpec
Testes que acessam rede
introduçãoTestes que acessam rede
Testes lentos
introduçãoTestes que acessam rede
Testes lentosTestes quebradiços
introduçãoTestes que acessam rede
Testes lentosTestes quebradiços
Não poder testar sem rede
introduçãoTestes que acessam rede
app/services/criador_pokemon.rb
class CriadorPokemon# ...def criar Pokemon.create(nome: nome)end
private # ... def cria_info resposta = Net::HTTP.get(endpoint) @info = JSON.parse(resposta) endend
spec/services/criador_pokemon_spec.rb
describe 'pokemon criado' do before do criador_pokemon.criar end
subject do Pokemon.last end
it 'possui o nome correto' do expect(subject.nome).to eq('Charizard') endend
webmockTestes que acessam rede
webmock: feedback rápidoTestes que acessam rede
bash
Failure/Error: CriadorPokemon.new(6) WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request: GET http://pokeapi.co/api/v1/pokemon/6/ with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Host'=>'pokeapi.co', 'User-Agent'=>'Ruby'}
You can stub this request with the following snippet:
stub_request(:get, "http://pokeapi.co/api/v1/pokemon/6/"). with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Host'=>'pokeapi.co', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
webmock: forjando a respostaTestes que acessam rede
spec/services/criador_pokemon_spec.rb
describe 'pokemon criado' do
before do body = '{' \ ' "name": "Charizard"' \ '}' stub_request(:get, 'http://pokeapi.co/api/v1/pokemon/6/') .to_return(status: 200, body: body, headers: {}) criador_pokemon.criar endend
webmock: forjando com cURLTestes que acessam rede
bash
$ curl -is http://pokeapi.co/api/v1/pokemon/6/ > \spec/fixtures/services/criador_pokemon/resposta.txt
spec/services/criador_pokemon_spec.rb
describe 'pokemon criado' do
before do caminho_arquivo = 'spec/fixtures/services/criador_pokemon/resposta.txt' arquivo_resposta = File.new(caminho_arquivo) stub_request(:get, 'http://pokeapi.co/api/v1/pokemon/6/') .to_return(arquivo_resposta) criador_pokemon.criar endend
vcrTestes que acessam rede
vcr: con!guraçãoTestes que acessam rede
spec/support/vcr.rb
VCR.configure do |c| c.cassette_library_dir = 'spec/fixtures/vcr_cassettes' c.hook_into :webmockend
vcr: feedback rápidoTestes que acessam rede
bash
Failure/Error: CriadorPokemon.new(6)VCR::Errors::UnhandledHTTPRequestError:
===================================================================== An HTTP request has been made that VCR does not know how to handle: GET http://pokeapi.co/api/v1/pokemon/6/
There is currently no cassette in use. There are a few ways you can configure VCR to handle this request:
...
vcr: forjando a respostaTestes que acessam rede
spec/services/criador_pokemon_spec.rb
describe 'pokemon criado' do before do VCR.use_cassette('CriadorPokemon/criar') do criador_pokemon.criar end end
#...
it 'possui o nome correto' do expect(subject.nome).to eq('Charizard') endend
vcr: RSpec metadataTestes que acessam rede
spec/support/vcr.rb
VCR.configure do |c|
# ... c.configure_rspec_metadata!end
spec/spec_helper.rb
RSpec.configure do |config|
# ... config.treat_symbols_as_metadata_keys_with_true_values = trueend
spec/services/criador_pokemon_spec.rb
describe 'pokemon criado', :vcr do before do criador_pokemon.criar end
#...
it 'possui o nome correto' do expect(subject.nome).to eq('Charizard') endend
factory_girl
!xtures X factoriesfactory_girl
criando uma factoryfactory_girl
spec/factories/usuarios.rb
FactoryGirl.define do factory :usuario do nome 'Mauro' email 'mauro@helabs.com.br' endend
console rails
FactoryGirl.create(:usuario)FactoryGirl.create(:usuario, email: 'mauro@helabs.com.br')
con!gurandofactory_girl
spec/spec_helper.rb
RSpec.configure do |config| # ... config.include FactoryGirl::Syntax::Methodsend
Em um teste qualquer
let!(:artigo) do create(:artigo)end
attributes_forfactory_girl
spec/controllers/posts_controller_spec.rb
describe "POST 'create'" do let(:params) do { artigo: { titulo: 'Meu titulo', conteudo: 'Conteudo do artigo' } } endend
spec/controllers/posts_controller_spec.rb
describe "POST 'create'" do let(:params) do { artigo: attributes_for(:artigo) } endend
herançafactory_girl
spec/factories/artigos.rb
factory :artigo do titulo 'Diversas dicas do RSpec' conteudo 'Conteúdo de Diversas dicas do RSpec'
factory :artigo_aprovado do aprovado true end
factory :artigo_nao_aprovado do aprovado false endend
console rails
FactoryGirl.create(:artigo_aprovado)FactoryGirl.create(:artigo_nao_aprovado)
traitsfactory_girl
spec/factories/artigos.rb
factory :artigo do titulo 'Diversas dicas do RSpec' conteudo 'Conteúdo de Diversas dicas do RSpec'
trait :aprovado do aprovado true end
trait :nao_aprovado do aprovado false endend
console rails
FactoryGirl.create(:artigo, :aprovado)FactoryGirl.create(:artigo, :nao_aprovado)
dependent attributesfactory_girl
spec/factories/artigos.rb
factory :artigo do titulo 'Diversas dicas do RSpec' conteudo { "Conteúdo do artigo #{titulo}. Aprovado: #{aprovado}" }end
sequencefactory_girl
spec/factories/artigos.rb
factory :artigo do sequence(:titulo) { |n| "Diversas dicas do RSpec #{n}" } conteudo { "Conteúdo do artigo #{titulo}. Aprovado: #{aprovado}" }end
associaçõesfactory_girl
console rails
usuario = FactoryGirl.create(:usuario)FactoryGirl.create(:artigo, usuario: usuario)
spec/factories/artigos.rb
factory :artigo do titulo 'Diversas dicas do RSpec' conteudo { "Conteúdo do artigo #{titulo}. Aprovado: #{aprovado}" } usuarioend
aliasesfactory_girl
spec/factories/artigos.rb
factory :usuario, aliases: [:autor] do nome 'Mauro' email { "#{nome}@helabs.com.br" }end
strategiesfactory_girl
console rails
pokemon = FactoryGirl.build(:pokemon)
console rails
pokemon = FactoryGirl.build_stubbed(:pokemon)
lintfactory_girl
spec/support/factory_girl.rb
RSpec.configure do |config|
config.before(:suite) do begin DatabaseCleaner.start FactoryGirl.lint ensure DatabaseCleaner.clean end endend
timecop
app/models/pokemon.rb
class Pokemon < ActiveRecord::Base
scope :escolhidos_ontem, -> do where(escolhido_em: 1.day.ago.midnight..Time.zone.now.midnight) endend
spec/models/pokemon_spec.rb
describe '.escolhidos_ontem' do let!(:pokemon_escolhido_ontem) do create(:pokemon, escolhido_em: Time.zone.local(2014, 5, 16, 10, 45)) end
subject do Pokemon.escolhidos_ontem end
it 'tem o pokemon escolhido ontem' do expect(subject).to include(pokemon_escolhido_ontem) endend
spec/models/pokemon_spec.rb
describe '.escolhidos_ontem' do
# ...
it 'tem o pokemon escolhido ontem' do Timecop.freeze(Time.zone.local((2014, 5, 17, 10, 45)) do expect(subject).to include(pokemon_escolhido_ontem) end endend
simplecov
veri!cando a coberturasimplecov
spec/spec_helper.rb
require 'simplecov'SimpleCov.start 'rails'
Primeira linha do spec_helper.rb
O falso 100%simplecov
app/models/pokemon.rb
class Pokemon < ActiveRecord::Base
validates :nome, :id_nacional, presence: true scope :escolhidos_ontem, -> do where(escolhido_em: 1.day.ago.midnight..Time.zone.now.midnight) endend
Não teste associações, validações ou escopos do Active Record
simplecov
teste associações, validações e escopos do Active Record
simplecov
devo ter 100% de cobertura de testes?simplecov
shoulda-matchers
app/models/pokemon.rb
class Pokemon < ActiveRecord::Base
validates :nome, :id_nacional, presence: true validates :id_nacional, numericality: { only_integer: true, greater_than: 0 }end
spec/models/pokemon_spec.rb
describe 'validações' do
it { should validate_presence_of(:nome) } it { should validate_presence_of(:id_nacional) } it { should validate_numericality_of(:id_nacional).only_integer .is_greater_than(0) }end
os matchersshoulda-matchers
ActiveModel
os matchersshoulda-matchers
ActiveModelActiveRecord
os matchersshoulda-matchers
ActiveModelActiveRecord
ActionController
os matchersshoulda-matchers
além do shoulda-matchersshoulda-matchers
https://github.com/bmabey/email-spec
além do shoulda-matchersshoulda-matchers
https://github.com/bmabey/email-spechttps://github.com/philostler/rspec-sidekiq
além do shoulda-matchersshoulda-matchers
https://github.com/bmabey/email-spechttps://github.com/philostler/rspec-sidekiq
https://github.com/evansagge/mongoid-rspec
além do shoulda-matchersshoulda-matchers
Obrigado!
maurogeorge.com.br
top related