Date post: | 02-Aug-2015 |
Category: |
Technology |
Upload: | jo-cranford |
View: | 281 times |
Download: | 3 times |
The ratio of time spent reading (code) versus writing is well over 10 to 1 … (therefore) making it easy to read makes it easier to write.
“
Robert C Martin
RSpec.describe Survey do
it 'closed? returns true when the status is closed' do survey = Survey.new(status: :closed) expect(survey.closed?).to be true end
end
Describe Blocks• Convention is one spec file for each Ruby file
• describe block becomes ExampleGroup subclass with metadata
• Pass RSpec a class or a description
• described_class is equal to the class definition
• Each test -> instance of ExampleGroup subclass
RSpec.describe Survey do
describe '#close?' do
it 'is true when the status is closed' do survey = Survey.new(status: :closed) expect(survey.closed?).to be true end
it 'is false when the status is closed' do survey = Survey.new(status: :open) expect(survey.closed?).to be false end
end
end
RSpec.describe Survey do
describe '#close?' do
context 'when status is closed' do
it 'is true' do survey = Survey.new(status: :closed) expect(survey.closed?).to be true end
end
context 'when status is open' do
it 'is false' do survey = Survey.new(status: :open) expect(survey.closed?).to be false end
end
end
end
RSpec.describe SurveyHelper do describe '#survey_color_indicator' do
context 'when the survey is archived' do
context 'and status is active' do
it 'is archived' do survey = Survey.new(archived: true, status: :active) expect(survey_color_indicator(survey)).to eq('archived') end
end
context 'and status is closed' do
it 'is archived' do survey = Survey.new(archived: true, status: :closed) expect(survey_color_indicator(survey)).to eq('archived') end
end
end
context 'when the survey is not archived' do
context 'and status is active' do
it 'is active' do survey = Survey.new(archived: false, status: :active) expect(survey_color_indicator(survey)).to eq('active') end
end
context 'and status is closed' do
it 'is closed' do survey = Survey.new(archived: false, status: :closed) expect(survey_color_indicator(survey)).to eq('closed') end
end
end
end
end
RSpec.describe SurveyHelper do
describe '#survey_color_indicator' do
it 'is archived when the survey is archived and status is active' do survey = Survey.new survey.archived = true survey.status = :active expect(survey_color_indicator(survey)).to eq('archived') end
it 'is archived when the survey is archived and status is closed' do survey = Survey.new survey.archived = true survey.status = :closed expect(survey_color_indicator(survey)).to eq('archived') end
it 'is active when the survey is not archived and status is active' do survey = Survey.new survey.archived = false survey.status = :active expect(survey_color_indicator(survey)).to eq('active') end
it 'is closed when the survey is not archived and status is closed' do survey = Survey.new survey.archived = false survey.status = :closed expect(survey_color_indicator(survey)).to eq('closed') end
end
end
Before and AfterRSpec.describe SurveyAdminController do describe '#index' do before do Survey.create!(name: 'My Survey') get :index end
after do Survey.destroy_all end
it 'retrieves the surveys' do expect(assigns(:surveys).size).to eq 1 end endend
AroundRSpec.describe SurveyAdminController do describe '#index' do around do |example| Survey.create!(name: 'My Survey') get :index example.run Survey.destroy_all end
it 'retrieves the surveys' do expect(assigns[:surveys].size).to eq 1 end endend
Conditional BeforeRSpec.configure do |config| config.before(:example, :authorized => true) do log_in_as :authorized_user endend
describe Something, :authorized => true do # The before hook will run in before each example in this group.end
describe SomethingElse do it "does something", :authorized => true do # The before hook will run before this example. end
it "does something else" do # The hook will not run before this example. endend
RSpec.describe SurveyHelper do describe '#survey_color_indicator' do before do @survey = Survey.new end
context 'when the survey is archived' do before do @survey.archived = true end
context 'and status is active' do before do @survey.status = :active end
it 'is archived' do expect(survey_color_indicator(@survey)).to eq('archived') end end
context 'and status is closed' do before do @survey.status = :closed end
it 'is archived' do expect(survey_color_indicator(@survey)).to eq('archived') end end end endend
Instance Variables Antipattern
• Evaluated for every single test-> slows tests down
• Spring into existence the first time that they are evaluated-> subtle bugs can creep in
• Can’t be overridden in nested blocks-> duplicated code
RSpec.describe SurveyHelper do describe '#survey_color_indicator' do before do @survey = Survey.new end
context 'when the survey is archived' do before do @survey.archived = true end
context 'and status is active' do before do @survey.status = :active end
it 'is archived' do expect(survey_color_indicator(@survey)).to eq('archived') end end
context 'and status is closed' do before do @survey.status = :closed end
it 'is archived' do expect(survey_color_indicator(@survey)).to eq('archived') end end end endend
Let vs @RSpec.describe SurveyHelper do describe '#survey_color_indicator' do let(:survey) { Survey.new(archived: is_archived, status: status) }
context 'when the survey is archived' do let(:archived) { true }
context 'and status is active' do let(:status) { :active }
it 'is archived' do expect(survey_color_indicator(survey)).to eq('archived') end end
context 'and status is closed' do let(:status) { :closed }
it 'is archived' do expect(survey_color_indicator(survey)).to eq('archived') end end end endend
Nested letsRSpec.describe SurveyAdminController do describe '#update' do let(:survey) { Survey.create!(name: 'My Survey') } let(:params) { { survey_id: survey.id } }
before do post :update, params end
context 'when no updates are specified' do it 'does not change the survey' do expect(survey.name).to eq 'My Survey' end end
context 'when the survey name is changed' do let(:params) { { survey_id: survey.id, name: 'New Name' } }
it 'updates the name' do expect(survey.name).to eq 'New Name' end end endend
let!describe SurveyAdminController do describe '#index' do let!(:survey) { Survey.create!(name: 'My Survey') }
before do get :index end
after do Survey.destroy_all end
it 'retrieves the surveys' do expect(assigns[:surveys]).to include(survey) end endend
Execution OrderRSpec.describe Something do let!(:some_variable) do # First end
before do # Second end
describe 'Nested block' do let!(:nested_variable) do # Third end
before do # Fourth end
let!(:another_one) do # Fifth end endend
Subject• Originally introduced to allow one line syntax
• Recommended by Better Specs to DRY tests
• Sometimes difficult to figure out what the subject of a test actually is
• Proceed with caution …
Implicit and Explicit Subject# Explicitdescribe Person do subject { Person.new(:birthdate => 19.years.ago) } it "should be eligible to vote" do subject.should be_eligible_to_vote # ^ ^ explicit reference to subject not recommended endend
# Implicit subject => { Person.new }.describe Person do it "should be eligible to vote" do subject.should be_eligible_to_vote # ^ ^ explicit reference to subject not recommended endend
One Line Syntax
describe SurveyResults::Employee do let(:id) { 123 } let(:email) { "[email protected]" }
subject { described_class.new(id: id, email: email) }
it { is_expected.to have_attributes(id: 123) } it { is_expected.to have_attributes(email: "[email protected]") } it { is_expected.to have_attributes(submission: nil) }end
Summary• We all spend a lot of time reading and understanding code
-> Writing clearer code and tests helps us go faster
• Nested describe and context blocks separate tests into logical blocks
• Use before to group common set up code
• let over @instance_variables to keep tests DRY, clear and performant
• let! behaves like a before, be aware of the order of execution
• Explicit subject and one line syntax make tests more succinct
• Avoid compromising readability
References• Better Specs
http://betterspecs.org/
• RSpec docshttp://www.relishapp.com/rspec/rspec-core/v/3-0/docs
• RSpec source codehttps://github.com/rspec/rspec-core
• RSpec: It’s not actually magic (RailsConf US 2015)https://www.youtube.com/watch?v=Libc0-0TRg4