In RSpec, what's the difference between it_behaves_like
and include_examples
?
The documentation says:
include_examples
— include(s) the examples in the current context
it_behaves_like "name"
— include(s) the examples in a nested context
But what does this actually mean? Replacing one with the other doesn't seem to have an effect on whether my tests pass or fail. Is there a reason to prefer one over the other in some situations?
Also, are it_should_behave_like
and it_behaves_like
just synonyms?
According to the rspec source code, “context” is just a alias method of “describe”, meaning that there is no functional difference between these two methods. However, there is a contextual difference that'll help to make your tests more understandable by using both of them.
The word describe is an RSpec keyword. It is used to define an “Example Group”. You can think of an “Example Group” as a collection of tests. The describe keyword can take a class name and/or string argument.
let generates a method whose return value is memoized after the first call. This is known as lazy loading because the value is not loaded into memory until the method is called. Here is an example of how let is used within an RSpec test. let will generate a method called thing which returns a new instance of Thing .
Installing RSpecBoot up your terminal and punch in gem install rspec to install RSpec. Once that's done, you can verify your version of RSpec with rspec --version , which will output the current version of each of the packaged gems. Take a minute also to hit rspec --help and look through the various options available.
You probably know how to use describe
, context
, it
and specify
to clearly communicate one aspect of your code. The nested context provided by it_behaves_like
can be used to improve this communication with the reader.
I will base my example on the example given in the RSpec documentation for shared examples:
shared_examples "a collection" do context "initialized with 3 items" do it "says it has three items" do # ... end end end describe Array do it_behaves_like "a collection" include_examples "a collection" end
If you run RSpec with --format documentation
you get the following output:
Array behaves like a collection initialized with 3 items says it has three items initialized with 3 items says it has three items
So the difference is how the spec is read eg in case of a failure.
Which style you prefer is a question of aesthetics of how you like your specs to read. Furthermore you would suggest to always use the same style if you work in a team to improve consistency.
Also, are it_should_behave_like and it_behaves_like just synonyms?
Almost, the context is named differently. it should behave like ...
vs behaves like ...
. Again a question of aesthetics.
There is a difference in case you pass parameters to the shared_examples.
It's explained very well in a warning in their doc:
WARNING: When you include parameterized examples in the current context multiple times, you may override previous method definitions and last declaration wins. So if you have this kind of shared example (or shared context)
RSpec.shared_examples "some example" do |parameter| \# Same behavior is triggered also with either `def something; 'some value'; end` \# or `define_method(:something) { 'some value' }` let(:something) { parameter } it "uses the given parameter" do expect(something).to eq(parameter) end end RSpec.describe SomeClass do include_examples "some example", "parameter1" include_examples "some example", "parameter2" end
You're actually doing this (notice that first example will fail):
RSpec.describe SomeClass do \# Reordered code for better understanding of what is happening let(:something) { "parameter1" } let(:something) { "parameter2" } it "uses the given parameter" do \# This example will fail because last let "wins" expect(something).to eq("parameter1") end it "uses the given parameter" do expect(something).to eq("parameter2") end end
To prevent this kind of subtle error a warning is emitted if you declare multiple methods with the same name in the same context. Should you get this warning the simplest solution is to replace include_examples with it_behaves_like, in this way method overriding is avoided because of the nested context created by it_behaves_like
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With