SlideShare a Scribd company logo
1 of 23
Download to read offline
Let There Be Tests
Jo Cranford
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 After
RSpec.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
end
end
Around
RSpec.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
end
end
Conditional Before
RSpec.configure do |config|
config.before(:example, :authorized => true) do
log_in_as :authorized_user
end
end
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.
end
end
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
end
end
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
end
end
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
end
end
Nested lets
RSpec.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
end
end
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
end
end
Execution Order
RSpec.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
end
end
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
# Explicit
describe 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
end
end
# 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
end
end
One Line Syntax
describe SurveyResults::Employee do
let(:id) { 123 }
let(:email) { "man@moon.com" }
subject { described_class.new(id: id, email: email) }
it { is_expected.to have_attributes(id: 123) }
it { is_expected.to have_attributes(email: "man@moon.com") }
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 docs

http://www.relishapp.com/rspec/rspec-core/v/3-0/docs

• RSpec source code

https://github.com/rspec/rspec-core

• RSpec: It’s not actually magic (RailsConf US 2015)

https://www.youtube.com/watch?v=Libc0-0TRg4
P.S We’re hiring!
jo@cultureamp.com
Thanks!

More Related Content

Similar to R spec let there be tests

Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Workhorse Computing
 
Basics of Javascript
Basics of JavascriptBasics of Javascript
Basics of JavascriptUniverse41
 
Locality sensitive hashing
Locality sensitive hashingLocality sensitive hashing
Locality sensitive hashingSEMINARGROOT
 
Learning Perl 6
Learning Perl 6 Learning Perl 6
Learning Perl 6 brian d foy
 
Brixton Library Technology Initiative Week1 Recap
Brixton Library Technology Initiative Week1 RecapBrixton Library Technology Initiative Week1 Recap
Brixton Library Technology Initiative Week1 RecapBasil Bibi
 
Testing outside of the Ruby World
Testing outside of the Ruby WorldTesting outside of the Ruby World
Testing outside of the Ruby WorldJoseph Wilk
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl TechniquesDave Cross
 
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)Vysakh Sreenivasan
 
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...Philip Schwarz
 
jRuby: The best of both worlds
jRuby: The best of both worldsjRuby: The best of both worlds
jRuby: The best of both worldsChristopher Spring
 
Lecture 23
Lecture 23Lecture 23
Lecture 23rhshriva
 
Petitparser at the Deep into Smalltalk School 2011
Petitparser at the Deep into Smalltalk School 2011Petitparser at the Deep into Smalltalk School 2011
Petitparser at the Deep into Smalltalk School 2011Tudor Girba
 
Hidden treasures of Ruby
Hidden treasures of RubyHidden treasures of Ruby
Hidden treasures of RubyTom Crinson
 
Антипаттерны модульного тестирования
Антипаттерны модульного тестированияАнтипаттерны модульного тестирования
Антипаттерны модульного тестированияMitinPavel
 

Similar to R spec let there be tests (20)

Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!
 
Basics of Javascript
Basics of JavascriptBasics of Javascript
Basics of Javascript
 
Locality sensitive hashing
Locality sensitive hashingLocality sensitive hashing
Locality sensitive hashing
 
Lecture19-20
Lecture19-20Lecture19-20
Lecture19-20
 
Lecture19-20
Lecture19-20Lecture19-20
Lecture19-20
 
Ruby Classes
Ruby ClassesRuby Classes
Ruby Classes
 
Learning Perl 6
Learning Perl 6 Learning Perl 6
Learning Perl 6
 
Brixton Library Technology Initiative Week1 Recap
Brixton Library Technology Initiative Week1 RecapBrixton Library Technology Initiative Week1 Recap
Brixton Library Technology Initiative Week1 Recap
 
Values
ValuesValues
Values
 
Lucene And Solr Intro
Lucene And Solr IntroLucene And Solr Intro
Lucene And Solr Intro
 
Testing outside of the Ruby World
Testing outside of the Ruby WorldTesting outside of the Ruby World
Testing outside of the Ruby World
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)Testing Ruby with Rspec (a beginner's guide)
Testing Ruby with Rspec (a beginner's guide)
 
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
Function Applicative for Great Good of Palindrome Checker Function - Polyglot...
 
Variables
VariablesVariables
Variables
 
jRuby: The best of both worlds
jRuby: The best of both worldsjRuby: The best of both worlds
jRuby: The best of both worlds
 
Lecture 23
Lecture 23Lecture 23
Lecture 23
 
Petitparser at the Deep into Smalltalk School 2011
Petitparser at the Deep into Smalltalk School 2011Petitparser at the Deep into Smalltalk School 2011
Petitparser at the Deep into Smalltalk School 2011
 
Hidden treasures of Ruby
Hidden treasures of RubyHidden treasures of Ruby
Hidden treasures of Ruby
 
Антипаттерны модульного тестирования
Антипаттерны модульного тестированияАнтипаттерны модульного тестирования
Антипаттерны модульного тестирования
 

More from Jo Cranford

React on Rails - RailsConf 2017 (Phoenix)
 React on Rails - RailsConf 2017 (Phoenix) React on Rails - RailsConf 2017 (Phoenix)
React on Rails - RailsConf 2017 (Phoenix)Jo Cranford
 
Test Driven Development - For Girl Geeks Night Sydney
Test Driven Development - For Girl Geeks Night SydneyTest Driven Development - For Girl Geeks Night Sydney
Test Driven Development - For Girl Geeks Night SydneyJo Cranford
 
Testing javascriptwithjasmine sydjs
Testing javascriptwithjasmine sydjsTesting javascriptwithjasmine sydjs
Testing javascriptwithjasmine sydjsJo Cranford
 
Testing javascriptwithjasmine ddd-sydney
Testing javascriptwithjasmine ddd-sydneyTesting javascriptwithjasmine ddd-sydney
Testing javascriptwithjasmine ddd-sydneyJo Cranford
 
Why (I think) CoffeeScript Is Awesome
Why (I think) CoffeeScript Is AwesomeWhy (I think) CoffeeScript Is Awesome
Why (I think) CoffeeScript Is AwesomeJo Cranford
 
Less ismorewithcoffeescript webdirectionsfeb2012
Less ismorewithcoffeescript webdirectionsfeb2012Less ismorewithcoffeescript webdirectionsfeb2012
Less ismorewithcoffeescript webdirectionsfeb2012Jo Cranford
 

More from Jo Cranford (6)

React on Rails - RailsConf 2017 (Phoenix)
 React on Rails - RailsConf 2017 (Phoenix) React on Rails - RailsConf 2017 (Phoenix)
React on Rails - RailsConf 2017 (Phoenix)
 
Test Driven Development - For Girl Geeks Night Sydney
Test Driven Development - For Girl Geeks Night SydneyTest Driven Development - For Girl Geeks Night Sydney
Test Driven Development - For Girl Geeks Night Sydney
 
Testing javascriptwithjasmine sydjs
Testing javascriptwithjasmine sydjsTesting javascriptwithjasmine sydjs
Testing javascriptwithjasmine sydjs
 
Testing javascriptwithjasmine ddd-sydney
Testing javascriptwithjasmine ddd-sydneyTesting javascriptwithjasmine ddd-sydney
Testing javascriptwithjasmine ddd-sydney
 
Why (I think) CoffeeScript Is Awesome
Why (I think) CoffeeScript Is AwesomeWhy (I think) CoffeeScript Is Awesome
Why (I think) CoffeeScript Is Awesome
 
Less ismorewithcoffeescript webdirectionsfeb2012
Less ismorewithcoffeescript webdirectionsfeb2012Less ismorewithcoffeescript webdirectionsfeb2012
Less ismorewithcoffeescript webdirectionsfeb2012
 

Recently uploaded

Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 

Recently uploaded (20)

Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 

R spec let there be tests

  • 1. Let There Be Tests Jo Cranford
  • 2. 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
  • 3. 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
  • 4. 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
  • 5. 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
  • 6. 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
  • 7. 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
  • 8. Before and After RSpec.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 end end
  • 9. Around RSpec.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 end end
  • 10. Conditional Before RSpec.configure do |config| config.before(:example, :authorized => true) do log_in_as :authorized_user end end 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. end end
  • 11. 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 end end
  • 12. 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
  • 13. 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 end end
  • 14. 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 end end
  • 15. Nested lets RSpec.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 end end
  • 16. 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 end end
  • 17. Execution Order RSpec.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 end end
  • 18. 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 …
  • 19. Implicit and Explicit Subject # Explicit describe 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 end end # 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 end end
  • 20. One Line Syntax describe SurveyResults::Employee do let(:id) { 123 } let(:email) { "man@moon.com" } subject { described_class.new(id: id, email: email) } it { is_expected.to have_attributes(id: 123) } it { is_expected.to have_attributes(email: "man@moon.com") } it { is_expected.to have_attributes(submission: nil) } end
  • 21. 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
  • 22. References • Better Specs 
 http://betterspecs.org/ • RSpec docs
 http://www.relishapp.com/rspec/rspec-core/v/3-0/docs • RSpec source code
 https://github.com/rspec/rspec-core • RSpec: It’s not actually magic (RailsConf US 2015)
 https://www.youtube.com/watch?v=Libc0-0TRg4