SlideShare a Scribd company logo
INTRODUCTION TO UNIT TESTING
WITH RSPEC
by Artem Szubowicz
WHY TESTING?
JUNIOR DEVELOPER'S GUIDE
1. write code
2. write more code
3. write event more code!
4. run and check
5. if it crashes - debug & goto 3
6. goto 1
WHY TESTING?
Writing unit tests does not give you profit
immediately.
Instead, it does give you great profit in the
future.
WHY TESTING?
1. descriptive tests substitude documentation
2. finding bugs/errors is much faster
3. refactoring is safe
4. stubbing units still allows you to write logic for them
5. continuous integration
WHY TESTING?
JUNIOR DEVELOPER'S GUIDE
(for those who tried testing)
1. write code
2. write more code
3. cover 100% of code with tests!
4. run
5. if it crashes - debug
6. goto 1
WHY TESTING?
WHAT'S WRONG HERE?
do we need to test everything?
WHAT IS UNIT TESTING?
testing modules, classes and methods, written by us
WHY TESTING?
MIDDLE DEVELOPER'S GUIDE
1. write code
2. run
3. if it crashes - debug & fix it
4. cover it with tests
5. goto 1
A GOOD UNIT TEST IS
CONSISTENT
Same test, run with same code multiple times,
should always give same results.
A GOOD UNIT TEST IS
INDEPENDENT
Test should not change any other objects,
except of those, created in the test itself.
A GOOD UNIT TEST IS
DESCRIPTIVE
generated with --format documentationoption
emailValidator
isValid
for valid e-mail address
resolves with valid=true
for email address that is not valid
resolves with valid=false
resolves with correct reason
for error result
resolves with valid=false
resolves with correct reason
for service down
calls error with error code
isUnique
for non existing email
resolves with false
for existing email
resolves with true
RSPEC
RSpec is a framework for unit-testing in Ruby
RSPEC: CODE EXAMPLE
RSpec.describe OrderBuilder, type: :model do
let(:user) { create :user }
let(:dish) { create :dish }
let(:date) { random_day }
subject(:order_builder) { OrderBuilder.new(user, Date.parse(date)) }
describe '#initialize' do
context 'with date in the past' do
let(:date) { random_day_in_past }
it { expect { subject }.to raise_error(ArgumentError, 'Cannot place order i
end
context 'with date in future' do
let(:date) { 'Sunday' }
RSPEC: DIRECTORY STRUCTURE
RSpec tests (called "specs") are usually placed
in the spec/directory with the same
structure, as app/directory structure.
spec
├── controllers
│   ├── orders_controller_spec.rb
│   ├── restaurants_controller_spec.rb
│   ├── user
│   │   └── orders_controller_spec.rb
│   └── users_controller_spec.rb
├── factories
│   ├── orders.rb
│   ├── restaurants.rb
│   └── users.rb
├── models
│   ├── ability_spec.rb
│   ├── order_spec.rb
│   ├── restaurant_spec.rb
│   └── user_spec.rb
├── rails_helper.rb
├── spec_helper.rb
└── support
├── controller_helpers.rb
├── database_cleaner.rb
├── factory_girl.rb
└── request_helpers.rb
RSPEC: DIRECTORY STRUCTURE
factoriesand supportdirectories are specific to RSpec
RSPEC: DIRECTORY STRUCTURE
RSpec will look for files, whose names end
with _spec.rband run them.
RSPEC: TEST COMPONENTS
describe
subject
let
context
it
expect
create (FactoryGirl)
RSPEC: TEST COMPONENTS
Each test should start with
RSpec.describe, to allow usage of all
other components.
RSPEC: DESCRIBE
Groups test cases and defines the type of
subject being tested.
RSpec.describe OrdersController do
describe '#index' do
# ...
end
end
RSpec.describe OrderBuilder, type: :model do
describe '#initialize' do
# ...
end
end
adding a type for subject may add extra features
RSPEC: SUBJECT
Sets the subject being tested.
subject(:order_builder) { OrderBuilder.new(user, Date.parse(date)) }
# use `order_builder` variable
subject { -> { order_builder.place_order(order_params) } }
# use `subject` variable
RSPEC: LET
Defines a variable, whose value will be
calculated when being used.
let(:user) { create :user }
let(:dish) { create :dish }
# `random_day` method will be called when using `date` variable only
let(:date) { random_day }
# use `user`, `dish` and `date` variables
RSPEC: LET!
Defines a variable with a value immediately.
# `create :user` will be called right now
let!(:user) { create :user }
# use `user` variable
RSPEC: CONTEXT
Groups tests by the environment, test subject
is being used in. Used for the same subject, but
with different input/dependant values.
context 'with date in the past' do
let(:date) { random_day_in_past }
# here the `date` will equal to `random_day_in_past` call result
end
context 'with date in future' do
let(:date) { 'Sunday' }
# and here `date` will equal to `'Sunday'`
end
RSPEC: IT
Specifies test case' body.
it { expect { subject }.to raise_error(ArgumentError, 'Cannot place order in the
# `it` can also have a description
it "does not create order if user has one already" do
user.orders << create(:order, order_date: date)
expect(order_builder.order).to eq(user.orders.first)
end
RSPEC: EXPECT
Specifies test assertion.
expect { subject }.to raise_error(ArgumentError, 'Cannot place order at the weeke
expect { subject.method }.to change(Order.count).by(3)
# when used with `subject` method call:
# subject { order.dishes.count }
is_expected.not_to eq(0)
MOCKING OBJECTS
Stub any object, which potentially changes
externals outside the test, with a mock.
FACTORYGIRL
Allows to create mock objects. But requires
describing them in
spec/factories/FACTORY_NAME.rb
FACTORYGIRL: FACTORY
FactoryGirl.define do
factory :dish do |f|
f.name { Faker::Team.creature }
f.price { Random::rand(0 .. 100) }
f.description 'tasty dish'
f.kind :main_course
association :restaurant
end
end
FACTORYGIRL: CREATE
# uses factory named `dish`
let(:order) { create :dish }
Introduction to unit testing
UNIT TESTING BEST PRACTICES
BEST PRACTICES
GIVEN-WHEN-THENSTRUCTURE
# Given:
user.orders << create(:order, order_date: date)
# When:
order_builder.place_order!
# Then:
expect(Order.today.count).to eq(2)
BEST PRACTICES
GIVEN-WHEN-THENSTRUCTURE
# Given:
let(:user) { create :user }
let(:orders) { [ order1, order2 ] }
# When + Then:
expect(user.place(orders)).to change(Order.count).by(2)
BEST PRACTICES
INFORMATIVE MESSAGES
describe OrderBuilder do
describe '#place_order!' do
subject { -> { order_builder.place_order! } }
context 'with no orders' do
it 'places nothing' do
expect { subject }.not_to change(Order.today.coun
end
end
context 'with one order' do
let(:orders) { [ create :order ] }
it 'places one order' do
expect { subject }.to change(Order.today.count).b
end
end
BEST PRACTICES
ONE ASSERTION PER `IT`
BEST PRACTICES
NO CONDITIONALS
All the situations must be checked by test
cases.
describe OrderBuilder do
describe '#place_order!' do
subject { -> { order_builder.place_order! } }
context 'with no orders' do
it 'places nothing' do
expect { subject }.not_to change(Order.today.coun
end
end
context 'with one order' do
let(:orders) { [ create :order ] }
it 'places one order' do
expect { subject }.to change(Order.today.count).b
end
end
BEST PRACTICES
NO LOOPS
Replace them with (multiple) tests.
it 'sets "delivered" status for all orders' do
expect(user.orders.today).to all(eq(Order.DELIVERED))
end
it 'does not set "delivered" status for any order' do
expect(user.orders.today).not_to all(eq(Order.DELIVERED))
end
BEST PRACTICES
NO EXCEPTION CATCHING
Test should either expect an exception or no
exception to be thrown.
it 'throws ArgumentException' do
expect { order_builder.place_order! }.to raise_exception(ArgumentError
end
it 'does not throw any exception' do
expect { order_builder.place_order! }.not_to raise_exception
end
BEST PRACTICES
If you face any of these in your tests:
conditionals
loops
exception handling
that means your tests need refactoring
BEST PRACTICES
SENIOR DEVELOPER'S GUIDE
(Test Driven Development)
1. write marvelous tests
2. write code
3. run tests
4. if they fail - fix the code
5. goto 1
THE END

More Related Content

PDF
Testing Ruby with Rspec (a beginner's guide)
PPTX
Rspec presentation
PPT
Ruby on Rails testing with Rspec
PDF
Testing JavaScript Applications
PDF
Unit Testing Express Middleware
PPTX
RSpec: What, How and Why
PDF
Intro to Unit Testing in AngularJS
PDF
Test-Driven Development of AngularJS Applications
Testing Ruby with Rspec (a beginner's guide)
Rspec presentation
Ruby on Rails testing with Rspec
Testing JavaScript Applications
Unit Testing Express Middleware
RSpec: What, How and Why
Intro to Unit Testing in AngularJS
Test-Driven Development of AngularJS Applications

What's hot (20)

PDF
Reliable Javascript
PDF
AngularJS Unit Testing w/Karma and Jasmine
PPTX
Full Stack Unit Testing
PDF
Unit testing with mocha
PDF
node.js practical guide to serverside javascript
PDF
Automated testing with RSpec
PDF
Jasmine - why JS tests don't smell fishy
PDF
Unit testing JavaScript using Mocha and Node
PPTX
RSpec and Rails
PDF
PL/SQL Unit Testing Can Be Fun
PDF
Testing javascript in the frontend
PDF
Advanced Jasmine - Front-End JavaScript Unit Testing
PDF
Adventures In JavaScript Testing
PDF
Jasmine BDD for Javascript
ODP
Unit Testing and Coverage for AngularJS
ODP
Angular JS Unit Testing - Overview
PDF
Introduction to Protractor
PPT
Testing Javascript with Jasmine
PDF
Painless JavaScript Testing with Jest
PDF
Unit Testing Express and Koa Middleware in ES2015
Reliable Javascript
AngularJS Unit Testing w/Karma and Jasmine
Full Stack Unit Testing
Unit testing with mocha
node.js practical guide to serverside javascript
Automated testing with RSpec
Jasmine - why JS tests don't smell fishy
Unit testing JavaScript using Mocha and Node
RSpec and Rails
PL/SQL Unit Testing Can Be Fun
Testing javascript in the frontend
Advanced Jasmine - Front-End JavaScript Unit Testing
Adventures In JavaScript Testing
Jasmine BDD for Javascript
Unit Testing and Coverage for AngularJS
Angular JS Unit Testing - Overview
Introduction to Protractor
Testing Javascript with Jasmine
Painless JavaScript Testing with Jest
Unit Testing Express and Koa Middleware in ES2015
Ad

Viewers also liked (10)

PDF
Tworzenie grafiki 3D w android
PDF
Coding & Music Passion And Profession
PDF
Whoops! Where did my architecture go?
PDF
Data Access 2.0? Please welcome, Spring Data!
PDF
Whoops! Where did my architecture go?
PDF
Increasing developer procutivity with Mylyn (Devoxx 2010)
PDF
Clojure
PDF
Practical Protocols with Associated Types
PDF
Swift Delhi: Practical POP
PDF
Build Features, Not Apps
Tworzenie grafiki 3D w android
Coding & Music Passion And Profession
Whoops! Where did my architecture go?
Data Access 2.0? Please welcome, Spring Data!
Whoops! Where did my architecture go?
Increasing developer procutivity with Mylyn (Devoxx 2010)
Clojure
Practical Protocols with Associated Types
Swift Delhi: Practical POP
Build Features, Not Apps
Ad

Similar to Introduction to unit testing (20)

PDF
PDF
BDD style Unit Testing
PDF
2011-02-03 LA RubyConf Rails3 TDD Workshop
PDF
RSpec 3: The new, the old, the good
PDF
Ruby on rails rspec
KEY
Tdd for BT E2E test community
PDF
RSpec 2 Best practices
PDF
PDF
Rspec and Capybara Intro Tutorial at RailsConf 2013
PDF
WTF is TDD
PDF
Basic RSpec 2
PPTX
Rspec 101
PDF
RSpec Quick Reference
PPTX
Intro to TDD and BDD
ZIP
Rspec Tips
PDF
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
PDF
Rspec tutorial
PPTX
Testing, a pragmatic approach
BDD style Unit Testing
2011-02-03 LA RubyConf Rails3 TDD Workshop
RSpec 3: The new, the old, the good
Ruby on rails rspec
Tdd for BT E2E test community
RSpec 2 Best practices
Rspec and Capybara Intro Tutorial at RailsConf 2013
WTF is TDD
Basic RSpec 2
Rspec 101
RSpec Quick Reference
Intro to TDD and BDD
Rspec Tips
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
Rspec tutorial
Testing, a pragmatic approach

Recently uploaded (20)

PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
Nekopoi APK 2025 free lastest update
PDF
System and Network Administraation Chapter 3
PPTX
Introduction to Artificial Intelligence
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Understanding Forklifts - TECH EHS Solution
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
AI in Product Development-omnex systems
PDF
medical staffing services at VALiNTRY
PDF
top salesforce developer skills in 2025.pdf
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PTS Company Brochure 2025 (1).pdf.......
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Operating system designcfffgfgggggggvggggggggg
Softaken Excel to vCard Converter Software.pdf
Odoo Companies in India – Driving Business Transformation.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
Nekopoi APK 2025 free lastest update
System and Network Administraation Chapter 3
Introduction to Artificial Intelligence
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Understanding Forklifts - TECH EHS Solution
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
wealthsignaloriginal-com-DS-text-... (1).pdf
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Design an Analysis of Algorithms I-SECS-1021-03
AI in Product Development-omnex systems
medical staffing services at VALiNTRY
top salesforce developer skills in 2025.pdf

Introduction to unit testing

  • 1. INTRODUCTION TO UNIT TESTING WITH RSPEC by Artem Szubowicz
  • 2. WHY TESTING? JUNIOR DEVELOPER'S GUIDE 1. write code 2. write more code 3. write event more code! 4. run and check 5. if it crashes - debug & goto 3 6. goto 1
  • 3. WHY TESTING? Writing unit tests does not give you profit immediately. Instead, it does give you great profit in the future.
  • 4. WHY TESTING? 1. descriptive tests substitude documentation 2. finding bugs/errors is much faster 3. refactoring is safe 4. stubbing units still allows you to write logic for them 5. continuous integration
  • 5. WHY TESTING? JUNIOR DEVELOPER'S GUIDE (for those who tried testing) 1. write code 2. write more code 3. cover 100% of code with tests! 4. run 5. if it crashes - debug 6. goto 1
  • 6. WHY TESTING? WHAT'S WRONG HERE? do we need to test everything?
  • 7. WHAT IS UNIT TESTING? testing modules, classes and methods, written by us
  • 8. WHY TESTING? MIDDLE DEVELOPER'S GUIDE 1. write code 2. run 3. if it crashes - debug & fix it 4. cover it with tests 5. goto 1
  • 9. A GOOD UNIT TEST IS CONSISTENT Same test, run with same code multiple times, should always give same results.
  • 10. A GOOD UNIT TEST IS INDEPENDENT Test should not change any other objects, except of those, created in the test itself.
  • 11. A GOOD UNIT TEST IS DESCRIPTIVE generated with --format documentationoption emailValidator isValid for valid e-mail address resolves with valid=true for email address that is not valid resolves with valid=false resolves with correct reason for error result resolves with valid=false resolves with correct reason for service down calls error with error code isUnique for non existing email resolves with false for existing email resolves with true
  • 12. RSPEC RSpec is a framework for unit-testing in Ruby
  • 13. RSPEC: CODE EXAMPLE RSpec.describe OrderBuilder, type: :model do let(:user) { create :user } let(:dish) { create :dish } let(:date) { random_day } subject(:order_builder) { OrderBuilder.new(user, Date.parse(date)) } describe '#initialize' do context 'with date in the past' do let(:date) { random_day_in_past } it { expect { subject }.to raise_error(ArgumentError, 'Cannot place order i end context 'with date in future' do let(:date) { 'Sunday' }
  • 14. RSPEC: DIRECTORY STRUCTURE RSpec tests (called "specs") are usually placed in the spec/directory with the same structure, as app/directory structure.
  • 15. spec ├── controllers │   ├── orders_controller_spec.rb │   ├── restaurants_controller_spec.rb │   ├── user │   │   └── orders_controller_spec.rb │   └── users_controller_spec.rb ├── factories │   ├── orders.rb │   ├── restaurants.rb │   └── users.rb ├── models │   ├── ability_spec.rb │   ├── order_spec.rb │   ├── restaurant_spec.rb │   └── user_spec.rb ├── rails_helper.rb ├── spec_helper.rb └── support ├── controller_helpers.rb ├── database_cleaner.rb ├── factory_girl.rb └── request_helpers.rb RSPEC: DIRECTORY STRUCTURE factoriesand supportdirectories are specific to RSpec
  • 16. RSPEC: DIRECTORY STRUCTURE RSpec will look for files, whose names end with _spec.rband run them.
  • 18. RSPEC: TEST COMPONENTS Each test should start with RSpec.describe, to allow usage of all other components.
  • 19. RSPEC: DESCRIBE Groups test cases and defines the type of subject being tested. RSpec.describe OrdersController do describe '#index' do # ... end end RSpec.describe OrderBuilder, type: :model do describe '#initialize' do # ... end end adding a type for subject may add extra features
  • 20. RSPEC: SUBJECT Sets the subject being tested. subject(:order_builder) { OrderBuilder.new(user, Date.parse(date)) } # use `order_builder` variable subject { -> { order_builder.place_order(order_params) } } # use `subject` variable
  • 21. RSPEC: LET Defines a variable, whose value will be calculated when being used. let(:user) { create :user } let(:dish) { create :dish } # `random_day` method will be called when using `date` variable only let(:date) { random_day } # use `user`, `dish` and `date` variables
  • 22. RSPEC: LET! Defines a variable with a value immediately. # `create :user` will be called right now let!(:user) { create :user } # use `user` variable
  • 23. RSPEC: CONTEXT Groups tests by the environment, test subject is being used in. Used for the same subject, but with different input/dependant values. context 'with date in the past' do let(:date) { random_day_in_past } # here the `date` will equal to `random_day_in_past` call result end context 'with date in future' do let(:date) { 'Sunday' } # and here `date` will equal to `'Sunday'` end
  • 24. RSPEC: IT Specifies test case' body. it { expect { subject }.to raise_error(ArgumentError, 'Cannot place order in the # `it` can also have a description it "does not create order if user has one already" do user.orders << create(:order, order_date: date) expect(order_builder.order).to eq(user.orders.first) end
  • 25. RSPEC: EXPECT Specifies test assertion. expect { subject }.to raise_error(ArgumentError, 'Cannot place order at the weeke expect { subject.method }.to change(Order.count).by(3) # when used with `subject` method call: # subject { order.dishes.count } is_expected.not_to eq(0)
  • 26. MOCKING OBJECTS Stub any object, which potentially changes externals outside the test, with a mock.
  • 27. FACTORYGIRL Allows to create mock objects. But requires describing them in spec/factories/FACTORY_NAME.rb
  • 28. FACTORYGIRL: FACTORY FactoryGirl.define do factory :dish do |f| f.name { Faker::Team.creature } f.price { Random::rand(0 .. 100) } f.description 'tasty dish' f.kind :main_course association :restaurant end end
  • 29. FACTORYGIRL: CREATE # uses factory named `dish` let(:order) { create :dish }
  • 31. UNIT TESTING BEST PRACTICES
  • 32. BEST PRACTICES GIVEN-WHEN-THENSTRUCTURE # Given: user.orders << create(:order, order_date: date) # When: order_builder.place_order! # Then: expect(Order.today.count).to eq(2)
  • 33. BEST PRACTICES GIVEN-WHEN-THENSTRUCTURE # Given: let(:user) { create :user } let(:orders) { [ order1, order2 ] } # When + Then: expect(user.place(orders)).to change(Order.count).by(2)
  • 34. BEST PRACTICES INFORMATIVE MESSAGES describe OrderBuilder do describe '#place_order!' do subject { -> { order_builder.place_order! } } context 'with no orders' do it 'places nothing' do expect { subject }.not_to change(Order.today.coun end end context 'with one order' do let(:orders) { [ create :order ] } it 'places one order' do expect { subject }.to change(Order.today.count).b end end
  • 36. BEST PRACTICES NO CONDITIONALS All the situations must be checked by test cases. describe OrderBuilder do describe '#place_order!' do subject { -> { order_builder.place_order! } } context 'with no orders' do it 'places nothing' do expect { subject }.not_to change(Order.today.coun end end context 'with one order' do let(:orders) { [ create :order ] } it 'places one order' do expect { subject }.to change(Order.today.count).b end end
  • 37. BEST PRACTICES NO LOOPS Replace them with (multiple) tests. it 'sets "delivered" status for all orders' do expect(user.orders.today).to all(eq(Order.DELIVERED)) end it 'does not set "delivered" status for any order' do expect(user.orders.today).not_to all(eq(Order.DELIVERED)) end
  • 38. BEST PRACTICES NO EXCEPTION CATCHING Test should either expect an exception or no exception to be thrown. it 'throws ArgumentException' do expect { order_builder.place_order! }.to raise_exception(ArgumentError end it 'does not throw any exception' do expect { order_builder.place_order! }.not_to raise_exception end
  • 39. BEST PRACTICES If you face any of these in your tests: conditionals loops exception handling that means your tests need refactoring
  • 40. BEST PRACTICES SENIOR DEVELOPER'S GUIDE (Test Driven Development) 1. write marvelous tests 2. write code 3. run tests 4. if they fail - fix the code 5. goto 1