In the lecture title of today, I acknowledged the eagerness on part of the students to get started with their coveted projects after their 'ordeal' of doing RoR for the past week. I clarified the raison d'être of RoR being part and parcel of the course and how RSpec is a logical extension of that initiative.
- What does RSpec stand for? Why is it not called QSpec or YSpec?
- How does RSpect relate to the various testing categories(unit, integration, system, acceptance test)
- The differences of the various categories were exemplified by means of a ball pen manufacturer that needs to manufacture these items according to a variety of specifications and how the nature of testing would vary accordingly.
- Software development processes were introduced and discussed: TDD - BDD - DDD in order to relate RSpec accordingly.
During the enumeration of these different concepts, I made it a point to contrast the idealistic academic nature of these with the practical nature found in the wild. In summary, life is a jungle of chaos.
Incorporate RSpec
as denoted over
here.
Running rspec --init
will create:
.rspec
spec/spec_helper
.rspec
allows us to add a variety of command line options that we would
like to run by default. Here is a sample:
--color
--require spec_helper
The first option ensures that it will display the output in color whereas
the second option ensures it will require spec_helper.rb
for every file we
create under spec/
folder.
We can then simply run rspec
in the command line and it will run all
accordingly.
Here is a link
for more options to explore!
Incorporate RSpec
as denoted over
here.
rails generate rspec:install
In addition to the files above, it will also create spec/rails_helper.rb
that
ensures your Rails
environment is loaded as it runs the spec
s.
Once the students have had the big picture of where/what is RSpec, I delved in the specifics:
describe
: Pertains to describing the functionality under consideration. For unit testing, this would be class/instance methods which we allude to by nesting the keyworddescribe
.context
: Pertains to a specific state under which the above described functionality is to behave. As such, it is generally nested as different cases underdescribe
it
: Pertains to coining the expectation of our testlet
: Pertains to declaring values as variables that we can use throughout our test suite. They are laizily loaded as theit
-clauses refer to them.subject
: Is a specific case oflet
that abstracts away from the specific class that we are testing in a consistent way. This convention is helpful to quickly understand what is being tested vs what are mere dependencies leveraged to realize the test objectives.before
: Pertains to introducing a certain state to our test suite. Theeach
variant will reload that same state foreach
test whereas theall
variant will load it once forall
the tests under consideration.after
: Used to clean up/tear down test suite state after it is over. It also has the two variants::each
and:all
.
It is important to note that before
and after
blocks
should be setup
such that we can run our tests in randomized order.
Here are concrete examples of these building blocks
:
RSpec.describe Auction, :type => :model do
let(:invalid_title) { nil }
let(:invalid_description) { nil }
let(:invalid_date) { nil }
subject {
described_class.new(title: "Anything", description: "Lorem ipsum",
start_date: DateTime.now, end_date: DateTime.now + 1.week)
}
it "is valid with valid attributes" do
expect(subject).to be_valid
end
it "is not valid without a title" do
subject.title = invalid_title
expect(subject).to_not be_valid
end
it "is not valid without a description" do
subject.description = invalid_description
expect(subject).to_not be_valid
end
it "is not valid without a start_date" do
subject.start_date = invalid_date
expect(subject).to_not be_valid
end
it "is not valid without a end_date" do
subject.end_date = invalid_date
expect(subject).to_not be_valid
end
end
All of the building blocks illustrated above are in actual fact ruby blocks
. You can acquaint yourself with the syntax here.
However, it is better practice to nest describe
in order to specify the
methods
under consideration:
Rspec.describe Auction do
let(:auctioneer) = { Auctioneer.new }
let(:clerk) = { Clerk.new }
let(:bidders) = { [Bidder.new] }
subject do
# kindly take note that this is a fictitious example that does not correspond with the current code base as `Auction` model has different attributes over here
described_class.new(auction: auctioneer, clerk: clerk, bidders: bidders)
end
before do
described_class.load_inventory
end
describe '#inventory' do
context 'For `Clerk`s' do
it 'is all' do
...
end
end
context 'For `Bidder`s' do
it 'is a subset as what has already been sold is excluded' do
end
end
end
describe '....' do
it '....' do
end
end
end
Above we first describe
Auction
as the main class
under discussion
followed by nesting a describe
of the instance method name of
#inventory
. In this example, we also provided a practical example of
context
, how it allows us to segment the various case scenarios that may
occur under a specific method depending on circumstances.
We went over the various matchers that RSpec provide to us:
- Identity matchers
- Comparison Matchers
- Class/Type Matchers
- True/False Matchers
Cucumber is a collaboration framework that erroneously has been regarded as
a testing framework. It allows us to write feature specs
in collaboration with
business
in order to minimize the chance of misunderstanding as well as
formerly document the main business logic/behaviour that our application is
to exhibit. Such formalization allows us to rewrite the application when
the need arises. Cucumber feature specs are written with a typical -
Given
- When
- Then
syntax as follows:
Feature: Trade reconciliation
Scenario: Trade logged against the wrong account
Given a trade logged on Mike's account
But the clearing house recorded it as Kim's trade
When the clearing house EOD report is reconciled against fills
Then the trade should be flagged as inconsistent
Such documentation is parsed by Gherkin
and applied to the underlying
Cucumber
steps that developers will create to implement the various tests.
As discussed before, RSpec
pertains to a specific form of TDD
, e.g.
BDD
and RSpec
provides the same notions, where:
describe
corresponds toGiven
context
corresponds toWhen
it
corresponds toThen