Unit testing with RSpec is a tool for testing Ruby code using a behavior-driven development approach. RSpec allows writing unit tests to test models, controllers, helpers and other code. Writing unit tests brings advantages like confidence in deploying code, documenting how code works, and catching errors. RSpec provides features for testing including describing test contexts and expectations, factories for test data, mocking and stubbing, and spying on method calls.
2. Types of Tests
Unit tests
Integration tests
End to End tests
Load tests
UI tests
Manual tests
3. What is a unit tests?
A unit test is a piece of a code (usually a method) that
invokes another piece of code and checks the
correctness of some assumptions after-ward. If the
assumptions turn out to be wrong, the unit test has
failed.
4. The impotance of unit tests
Unit testing makes projects a lot more effective at
delivering the correct solution in a predictable and
managed way.
5. The advantage of unit testing
Executing unit tests doesn't require the
application to be running.
It can be done before the whole application (or
module) is built.
More con dent in deploying code that is covered
by unit tests.
The unit tests document the code, and shows how
to use it.
6. The disadvantages of unit tests
It takes time to write them.
It takes time to run them (in sourcery it took us
more than 1 hour).
It takes time to maintain them after the code had
changed.
7. How to write good unit tests
Readability: Writing test code that is easy to
understand and communicates well
Maintainability: Writing tests that are robust and
hold up well over time
Automation: Writing tests that require little
setup and con guration (preferably none)
8. A word about TDD
TDD is "Test driven development"
The circle of TDD:
Test fail
Test pass
Refactor
Very helpful in some scenarios (I use it from time
to time).
9. RSpec
RSpec is a Behaviour-Driven Development tool for
Ruby programmers. BDD is an approach
to software development that combines Test-Driven
Development, Domain Driven Design,
and Acceptance Test-Driven Planning. RSpec helps
you do the TDD part of that equation,
focusing on the documentation and design aspects of
TDD.
11. Test sructure
context - the context of the tests
describe - description of what we are testing
it - what do we expect the result will be.
context 'divide' do
describe 'verify it divide non zero number' do
it 'should return a correct result' do
expect(CalcHelper.divide(8,4)).to eq(3)
end
end
end
12. What do we test?
Golden scenarion
Each case the method has, it means every if, loop,
switch and so on.
Unreasonable parameters
def divide(a,b)
return a/b unless b.zero?
end
13. describe 'verify it divide non zero number' do
it 'should return a correct result' do
expect(CalcHelper.divide(8,4)).to eq(3)
end
end
describe 'verify it divide zero number will not raise an excepti
it 'should return nil' do
expect(CalcHelper.divide(8,0)).to eq(nil)
end
end
describe 'verify the parameters' do
it 'should return nil' do
expect(CalcHelper.divide('blabla',6)).to eq(nil)
end
end
14. The result helps us understand
what went wrong
Failures:
1) CalcHelper divide verify it divide non zero number should
Failure/Error: expect(CalcHelper.divide(8,4)).to eq(3)
expected: 3
got: 2
(compared using ==)
# ./spec/helpers/test_helper.rb:11:in `block (4 levels) in
Finished in 0.89486 seconds (files took 12.31 seconds to load
1 example, 1 failure
15. What can we test with RSpec
Models
Controllers
Helpers
Anything else...
16. Testing models
RSpec.describe AccountExpert, type: :model do
context 'fields' do
it { should respond_to(:account) }
it { should respond_to(:user) }
it { should respond_to(:specialization_subcategory) }
it { should respond_to(:specialization_subject) }
end
context 'creation' do
it 'should succeed' do
acc = Account.create(org_name: 'account1')
AccountExpert.create(user: @user, account: acc)
expect(AccountExpert.count).to eq(1)
end
end
end
17. Test model validations
context 'User fields validations' do
it 'first_name' do
user.first_name = 'a' * 21
expect(user.valid?).to eq(false)
end
end
18. Test controllers
context 'admin' do
login_admin
describe 'index' do
it 'should get all activities' do
get :index
expect(response.status).to eq(200)
json_response = JSON.parse(response.body)
expect(json_response.count).to eq 1
expect(json_response[0]['name']).to eq 'Art'
expect(json_response[0]['department_name']).to
eq 'Music'
end
end
end
19. Test helpers
describe QuestionaireHelper, type: :helper do
context 'when questionaire exist' do
it 'should return questionaire' do
@questionaire.executed_at = Time.now
@questionaire.save!
res = QHelper.find_daily(DateTime.now,@station, -1)
expect(res.id).to eq @questionaire.id
end
end
end
20. before
before do
@org = create :organization, name: 'BGU'
@org2 = create :organization
end
before :each
before :all
after - usually used to clean up after tests. The
best way is to clean the DB after each test using
database_cleaner.
21. Factories
help us create data fast with mimimum code
FactoryGirl - now called FactoryBot
FactoryGirl.define do
factory :activity do
sequence(:name) { |i| "activity #{i}" }
organization "Spectory"
department "Dev"
end
end
activity = create :activity, name: 'dev meeting'
22. Mocking, stubing, Facking
Instead of calling the real code, we call a mock that
will return our expected result
allow(Helper).to receive(:call)
allow(Helper).to receive(:call).and_return(result)
allow(Helper).to receive(:call).with(param).
and_return(result)
25. Expect changes
Expect some code to change the state of some object
expect{Counter.increment}.to
change{Counter.count}.from(0).to(1)
26. Devise
Devise has gem that enable tests with RSpec
context 'instructor' do
login_instructor
describe 'index' do
it 'should get unauthorized error' do
get :index
expect(response.status).to eq(403)
end
end
end