Terminology
Let's wrangle this RSpec jargon!
Test structure
describe
describe
blocks are used to create example groups. You can nest describe
blocks.
The official definition:
The
describe
method creates an example group. Within the block passed todescribe
you can declare nested groups using thedescribe
orcontext
methods, or you can declare examples using theit
orspecify
methods.Under the hood, an example group is a class in which the block passed to
describe
orcontext
is evaluated. The blocks passed toit
are evaluated
in the context of an _instance _of that class.
https://relishapp.com/rspec/rspec-core/v/3-4/docs/example-groups/basic-structure-describe-it
RSpec.describe MyClass do
# prepend instance methods with #
describe "#my_instance_method" do
end
# prepend class methods with a .
describe ".my_class_method" do
end
end
context
context
blocks function like describe
blocks, but semantically, they're used to flesh out the various logical branches you're trying to test.
describe "GET #my_page_under_auth" do
subject { get :my_page_under_auth }
context "When a user is not logged in" do
it "does not access the page but redirects to sign in" do
...
end
end
context "When the user is logged in" do
before { login_the_user }
it "successfully gets page" do
expect(subject).to be_success
end
end
end
let
How to define variables in your RSpec tests
let(:sir_george) { Cat.create }
let!(:sweet_pea) { Cat.create }
# let does not create your variable immediately but waits for it to be called
# let! creates your variable immediately
# Cat.count => 1 right meow
Great read about why you should use let
to define your test variables (tldr -- they're faster!) https://www.ombulabs.com/blog/rails/rspec/ruby/let-vs-instance.html
before
Use a before block to do any necessary test set up. By default, this block runs before each expectation. You can use before(:all)
to only execute the before block once before all expectations. More on that here.
describe "GET #my_page_under_auth" do
context "When the user is logged in" do
before { login_the_user }
it "successfully gets page" do
expect(subject).to be_success
end
end
end
after
Opposite of before
block; it runs after all expectations. Use after
blocks to tear down any test setup.
after { User.destroy_all }
it
it "is active" do
expect(subject.active?).to be true
expect(subject).to be_active
end
# where subject is defined
it { is_expected.to be_active }
expectations
# expect to be a type of thing
expect(User.new).to be_an_instance_of(User)
# expect to include a module
expect(instance).to be_a_kind_of(MyModuleName)
#expect an instance or class to receive a method
expect(object).to receive(:some_wild_n_crazy_method)
# expect an action to have side effects
expect{ MyAction.call }.to change{ my_array.count }.from(1).to(2)
eq vs eql and matchers
Note the difference between eq
and eql
... one checks for string type equivalence, the other does not.
expect(1).to eq(1.0) # passes
expect(1).to eql(1.0) # fails
Read more here: https://relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/equality-matchers
Testing Data/Dependencies
double
Test object that mimics your objects and dependencies
stubs and mocks
I'm guilty of using these terms interchangeably, but essentially you mock out an interface and stub a canned response.
A stub...
Returns canned responses, avoiding any meaningful computation or I/O example:
allow(some_object).to receive(some_method).and_return(some_value)
While mocks...
Expects specific messages; will raise an error if it doesn’t receive them by the end of the example example:
expect(some_object).to receive(some_method).and_return(some_value)
Hat tip to rubyblog.pro for the definitions! Helped me to understand :)
factory
Generator for your test data! The Bot formerly known as FactoryGirl is the gem we'll be using for factory data.
fixture
File or static asset you test against