Describe VS Context in Rspec. Differences

describe vs context in rspec. Differences?

There is not much difference between describe and context. The difference lies in readability. I tend to use context when I want to separate specs based on conditions. I use describe to separate methods being tested or behavior being tested.

One main thing that changed in the latest RSpec is that "context" can no longer be used as a top-level method.

RSpec: describe, context, feature, scenario?

The context is an alias for describe, so they are functionally equivalent. You can use them interchangeably, the only difference is how your spec file reads. There is no difference in test output for example. The RSpec book says:

"We tend to use describe() for things and context() for context".

Personally I like to use describe, but I can see why people prefer context.

feature and scenario are a part of Capybara, and not RSpec, and are meant to be used for acceptance tests. feature is equivalent to describe / context, and scenario equivalent to it / example.

If you're writing acceptance tests with Capybara, use the feature / scenario syntax, if not use describe / it syntax.

Rails + Rspec: Patterns and Styles for it/describe/context descriptions?

Basically:

  • describe is for "something". "Something" can be a instance or class method, or an action in features specs. ".method_name" if it is a class method and "#method_name" if it is a instance method.
  • context is for describe a special case of spec (context is an alias for describe). Usually start with "when".
  • it is what does 'something'. Usually start with "should".

describe ".to_s"
context "when is a number"
it "convert the number in a string"
context "when is a string"
it "return the same object"

But this is not strictly. Here's a guidance:

  • https://www.relishapp.com/rspec/rspec-core/docs/example-groups/basic-structure-describe-it
  • http://blog.teamtreehouse.com/an-introduction-to-rspec
  • http://eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/

Contexts in rspec

The structure I use when defining rspec files (based on reading I've done on rspec) is that you use describes to describe specific functions, and context to talk about a specific context of state and/or path through the function.

Example class:

class MyClass
def self.my_class_method(bool)
if bool == true
return "Yes"
else
return "No"
end
end

def my_instance_method
today = Date.today
if today.month == 2 and today.day == 14
puts "Valentine's Day"
else
puts "Other"
end
end
end

As you can see, I've defined a class method and an instance method that do really silly and random functions. But the point is this: the class method will do something different based on the argument, and the instance method will do something different based on some outside factor: you need to test all these, and these are different contexts. But we will describe the functions in the rspec file.

Rspec file:

describe MyClass do
describe ".my_class_method" do
context "with a 'true' argument" do
it "returns 'Yes'." do
MyClass.my_class_method(true).should eq "Yes"
end
end

context "with a 'false' argument" do
it "returns 'No'." do
MyClass.my_class_method(false).should eq "No"
end
end
end

describe "#my_instance_method" do
context "on Feb 14" do
it "returns 'Valentine's Day'." do
Date.stub(:today) { Date.new(2012,2,14) }
MyClass.new.my_instance_method.should eq "Valentine's Day"
end
end

context "on a day that isn't Feb 14" do
it "returns 'Other'." do
Date.stub(:today) { Date.new(2012,2,15) }
MyClass.new.my_instance_method.should eq "Other"
end
end
end
end

So you can see the describe is for saying what method you're describing, and matches up with the name of a method in your class. The context is used to evaluate different conditions the method can be called in, or different states that affect the way the method works.

Hope this helps!

What's the difference between RSpec's subject and let? When should they be used or not?

Summary: RSpec's subject is a special variable that refers to the object being tested. Expectations can be set on it implicitly, which supports one-line examples. It is clear to the reader in some idiomatic cases, but is otherwise hard to understand and should be avoided. RSpec's let variables are just lazily instantiated (memoized) variables. They aren't as hard to follow as the subject, but can still lead to tangled tests so should be used with discretion.

The subject

How it works

The subject is the object being tested. RSpec has an explicit idea of the subject. It may or may not be defined. If it is, RSpec can call methods on it without referring to it explicitly.

By default, if the first argument to an outermost example group (describe or context block) is a class, RSpec creates an instance of that class and assigns it to the subject. For example, the following passes:

class A
end

describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end

You can define the subject yourself with subject:

describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end

You can give the subject a name when you define it:

describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end

Even if you name the subject, you can still refer to it anonymously:

describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end

You can define more than one named subject. The most recently defined named subject is the anonymous subject.

However the subject is defined,

  1. It's instantiated lazily. That is, the implicit instantiation of the described class or the execution of the block passed to subject doesn't happen until subject or the named subject is referred to in an example. If you want your explict subject to be instantiated eagerly (before an example in its group runs), say subject! instead of subject.

  2. Expectations can be set on it implicitly (without writing subject or the name of a named subject):

    describe A do
    it { is_expected.to be_an(A) }
    end

    The subject exists to support this one-line syntax.

When to use it

An implicit subject (inferred from the example group) is hard to understand because

  • It's instantiated behind the scenes.
  • Whether it's used implicitly (by calling is_expected without an explicit receiver) or explicitly (as subject), it gives the reader no information about the role or nature of the object on which the expectation is being called.
  • The one-liner example syntax doesn't have an example description (the string argument to it in the normal example syntax), so the only information the reader has about the purpose of the example is the expectation itself.

Therefore, it's only helpful to use an implicit subject when the context is likely to be well understood by all readers and there is really no need for an example description. The canonical case is testing ActiveRecord validations with shoulda matchers:

describe Article do
it { is_expected.to validate_presence_of(:title) }
end

An explict anonymous subject (defined with subject without a name) is a little better, because the reader can see how it's instantiated, but

  • it can still put the instantiation of the subject far from where it's used (e.g. at the top of an example group with many examples that use it), which is still hard to follow, and
  • it has the other problems that the implicit subject does.

A named subject provides an intention-revealing name, but the only reason to use a named subject instead of a let variable is if you want to use the anonymous subject some of the time, and we just explained why the anonymous subject is hard to understand.

So, legitimate uses of an explicit anonymous subject or a named subject are very rare.

let variables

How they work

let variables are just like named subjects except for two differences:

  • they're defined with let/let! instead of subject/subject!
  • they do not set the anonymous subject or allow expectations to be called on it implicitly.

When to use them

It's completely legitimate to use let to reduce duplication among examples. However, do so only when it doesn't sacrifice test clarity. The safest time to use let is when the let variable's purpose is completely clear from its name (so that the reader doesn't have to find the definition, which could be many lines away, to understand each example) and it is used in the same way in every example. If either of those things isn't true, consider defining the object in a plain old local variable or calling a factory method right in the example.

let! is risky, because it's not lazy. If someone adds an example to the example group that contains the let!, but the example doesn't need the let! variable,

  • that example will be hard to understand, because the reader will see the let! variable and wonder whether and how it affects the example
  • the example will be slower than it needs to be, because of the time taken to create the let! variablle

So use let!, if at all, only in small, simple example groups where it's less likely that future example writers will fall into that trap.

The single-expectation-per-example fetish

There is a common overuse of subjects or let variables that's worth discussing separately. Some people like to use them like this:

describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end

(This is a simple example of a method that returns a number for which we need two expectations, but this style can have many more examples/expectations if the method returns a more complicated value that needs many expectations and/or has many side effects that all need expectations.)

People do this because they've heard that one should have only one expectation per example (which is mixed up with the valid rule that one should only test one method call per example) or because they're in love with RSpec trickiness. Don't do it, whether with an anonymous or named subject or a let variable! This style has several problems:

  • The anonymous subject isn't the subject of the examples — the method is the subject. Writing the test this way screws up the language, making it harder to think about.
  • As always with one-line examples, there isn't any room to explain the meaning of the expectations.
  • The subject has to be constructed for each example, which is slow.

Instead, write a single example:

describe 'Calculator' do
describe '#calculate' do
it "returns a single-digit number" do
result = Calculator.calculate
expect(result).to be >= 0
expect(result).to be <= 9
end
end
end

Advantage to using describe/it over feature/scenario in specs? (besides syntactic sugar)

I think the question is a bit broad, but it is possible to answer with some advice and opinions based on my own experience.

  1. Is there any advantage to writing tests with describe/it over feature/scenario? (Besides syntactic sugar)

Not as far as I know. However, you may find some convenient test framework features are easier to implement in one scheme than another.


  1. By using Capybara's feature/scenario, does it slow down the test suite? (Compared to using RSpec's keywords)

Just using the keywords will not be a large factor in speed of processing. What kind of web driver and host simulation you are using will have a larger impact.


  1. What exactly are the tests I am writing (as explained in the code block)? Acceptance, unit, a combination?

I would call them acceptance tests. However, there is not always a clear dividing line, and you need to look at how the tests will be run, and how they will be used in your development process.

A mature development pipeline may have two or three separate test suites used for different purposes, and probably implemented using different test frameworks. You might want a set of very fast tests (usually unit tests) implemented to run as a quick automated test of new code commits for instance.


  1. Would writing tests like the above alone achieve higher coverage? (Our next goal is >80%)

The tests can exercise any user-accessible feature of the application, and any of your own code that is exercised can be considered covered. It is likely you can get higher than 80% C0 coverage (Ruby coverage tools don't usually provide deeper details such as C1), provided you do not have a lot of utility scripts or other code that is not user-accessible.


I suspect using a specific test framework's keywords will have minimal impact. However, using Capybara to acceptance test the application via the web interface is going to be much slower than running lower-level unit tests of individual modules.

Speed of tests can vary orders of magnitude. For tight unit tests around a fast module, I might expect to run 100 examples per second. On a web development project, I typically run 10-20 examples per second on unit tests, but maybe 1 example per second on acceptance tests (which is roughly the ballpark you are getting here). When using Capybara via a browser driver on a hosted copy of a site, I might expect to run one example in 10 seconds, so a suite with over 100 tests has to be run only for critical-path tests, such as versus release candidates.

What's the difference between include_examples and it_behaves_like?

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.

rspec shared examples vs shared context

shared_examples are tests written in a way that you can run them in multiple settings; extracting common behavior between objects.

it_behaves_like "a correct object remover" do
...
end

shared_contexts is any setup code that you can use to prepare a test case . This allows you to include test helper methods or prepare for the tests to run.

include_context "has many users to begin with"


Related Topics



Leave a reply



Submit