5. Changes
â New syntax does not use âmonkey patchingâ
â Examples are based on âexpectationsâ over objects
6. </>
What is it about?
# âshouldâ is âreplacedâ by âexpectâ
#
it âstores 5 in its attributeâ do
expect(subject.attribute).to eq 5
end
7. </>
In one line
# One-liner syntax changes accordingly
#
it { should eq 5 }
# Is written like...
#
it { is_expected.to eq 5 }
8. Migrating
â We can migrate our current specs to the new syntax
using github.com/yujinakayama/transpec
â In 3.1.z, thereâs still a mechanism to work with the old
syntax
10. Changes
â New syntax to stub methods
â Also a new syntax to set expectations on method
calls
11. </>
How to stub
# Old
instance.stub(:method)
instance.stub(:method).with(<arguments>)
instance.stub(:method).with(<arguments>).and_return(<something>)
# New
allow(instance).to receive(:method)
allow(instance).to receive(:method).with(<arguments>)
allow(instance).to receive(:method).with(<arguments>)...
12. </>
stub on any instance
# Old
Object.any_instance.stub(:method)
Object.any_instance.stub(:method).with(<arguments>)
Object.any_instance.stub(:method).with(<arguments>).and_...
# New
allow_any_instance_of(Object).to receive(:method)
allow_any_instance_of(Object).to receive(:method).with...
allow_any_instance_of(Object).to receive(:method).with...
15. </>
...on any instance
# Old
Object.any_instance.should_receive(:method)
Object.any_instance.should_receive(:method).with(<arguments>)
# New
expect_any_instance_of(Object).to receive(:method)
expect_any_instance_of(Object).to receive(:method).with...
18. </>
Many ways to skip an example
# Common way
#
it âstores 5 in its attributeâ, skip: true do
# Giving a reason for the output
#
it âstores 5 in its attributeâ, skip: âreasonâ do
# Shortcut
#
skip âstores 5 in its attributeâ do
20. Pending should make sense
â When an example inside a pending block passes,
RSpec will show an error
â This enforces that all pending blocks are pending for
a good reason
23. </>
Chaining
# You can chain multiple âmatchersâ
#
expect(subject.attribute).
to start_with(âhelloâ).and end_with(âworldâ)
expect(subject.attribute).
to start_with(âhelloâ).or start_with(âgoodbyeâ)
24. </>
The cooler way
# You can chain multiple âmatchersâ
#
expect(subject.attribute).
to start_with(âhelloâ) & end_with(âworldâ)
expect(subject.attribute).
to start_with(âhelloâ) | start_with(âgoodbyeâ)
26. </>
A matcher for structures
# âmatchâ can validate Hash and Array object structures
#
let(:structure) { {key: :value, other_key: :other_value} }
expect(structure).
to match({key: :value, other_key: :other_value})
# Still works for strings and regexps as before
28. </>
Match arrays
# =~ does not work anymore as an arrayâs content matcher
# Now
expect(ary).to contain_exactly(1, 2, 3)
# Also in its explicit version
expect(ary).to match_array([1, 2, 3])
30. </>
Cambios en el ser
# âbe_trueâ becomes âbe_truthyâ
expect(true).to be_truthy
# âbe_falseâ becomes âbe_falseyâ or âbe_falsyâ
expect(false).to be_falsey
expect(false).to be_falsy
# Having better semantics like in...
expect(nil).to be_falsey # instead of âbe_falseâ
31. </>
Cambios en el ser
expect(nil).to be_false # Fails
expect(nil).to be_falsey # Passes
expect(âhello worldâ).to be_true # Fails
expect(âhello worldâ).to be_truthy # Passes
36. </>
Why, oh lord, why?
it âstores 5 in its attribute if attribute b is 6 and
attribute c is 7â do
subject.attribute_b = 6
subject.attribute_c = 7
expect(subject.attribute).to eq 5
end
37. </>
Why not just...?
context âwhen attribute b is 6â do
# ...
context âand attribute c is 7â do
# ...
it âstores 5 in its attributeâ do
expect(subject.attribute).to eq 5
end
38. Analysis
â We can find good reasons not to do this:
â Itâs more code
â Itâs cumbersome
â Iâm just a lazy dog
â RSpec is about documentation.
â Documentation takes time.
â Good documentation takes even more time.
40. </>
This is not always a bad thing
it 'sets the attribute' do
subject.very_expensive_loading
expect(subject.attribute).to be_kind_of(Fixnum)
expect(subject.attribute).to eq(5)
end
41. </>
...but sometimes itâs unnecessary
it 'stores a fixnum in its attribute' do
expect(subject.attribute).to be_kind_of(Fixnum)
end
it 'its attributeâs fixnum is 5' do
expect(subject.attribute).to eq(5)
end
43. </>
ÂżWhat should we test for?
it 'sets the attribute to 5' do
expect(subject).to receive(:=).with(5) # Dramatization
subject.set_attribute(5)
end
# Instead of
it 'sets the attribute to 5' do
subject.set_attribute(5)
expect(subject.attribute).to eq 5
end
44. Analysis
â Not always unnecessary.
â When itâs an expensive operations like the ones that
require access to an outside service, thereâs no need
to test for its results. (Assume itâs already tested)
â Donât be scared to raise the coverage of the same
method.
46. </>
Change the subject when you can
it 'returns an arrayâ do
expect(subject.method).to be_kind_of(Array)
end
it 'returns an array that includes âfooâ' do
expect(subject.method).to include(:foo)
end
it 'returns an array with something else' do
# ...
47. </>
Change the subject when you can
describe '#methodâs return value' do
subject { instance.method }
it 'is an arrayâ do
expect(subject).to be_kind_of(Array)
end
it 'includes âfooâ' do
expect(subject).to include(:foo)
end
50. </>
And its little brother
it 'does somethingâ do
value_a = :foo
value_b = :boo
value_c = :cmon_bro
value_d = :you_are_killing_me
expect(instance.
method(value_a, value_b, value_c, value_d)).
to_not raise_error
end
51. </>
A relief in sight
let(:value_a) { :foo }
let(:value_b) { :boo }
let(:value_c) { :cmon_bro }
let(:value_d) { :you_are_killing_me }
it 'does somethingâ do
expect(instance.
method(value_a, value_b, value_c, value_d)).
to_not raise_error
end
56. Analysis
â create stores in the database.
â I mean, it stores in the database.
â Do we really need to persist?
â build is new without save
â The bad thing is that we have to run callbacks
manually or setting up our Factory to do so
â Is that bad, anyway?
58. </>
Look what a pretty request
# Letâs pretend âinstance.call_apiâ is a very expensive call
it 'parses the api responseâ do
expect(subject.parser(instance.call_api)).
to be_kind_of(Hash)
end
59. </>
Look what a pretty request
before do
allow(instance).to receive(:call_api).
and_return(<valid response from api>)
end
it 'parses the api responseâ do
expect(subject.parser(instance.call_api)).
to be_kind_of(Hash)
end
62. </>
Inherited behavior
class Foo < Bar
# âŠ
shared_examples_for Bar do
it âserves drinksâ
end
describe Foo do
it_behaves_like Bar
it âdoes whatever a foo doesâ
end
63. Analysis
â Reuse of examples (DRY).
â Consistent behavior across all subclasses.
â The idea is to only verify whatâs inherited; we donât
want to test private stuff.