What is the difference between be_true and be true in RSpec
According to this thread be_true
and be_false
are now known as be_truthy
and be_falsy
.
The basic difference between be true
and be_truthy
or be false
and be_falsy
is that be_falsy
/be_truthy
matcher passes if the "expected result"(i.e. any object) is(for be_falsy
)/ is not(for be_truthy
) nil
or false
, while on the other hand be true
and be false
use ==
for validating your "expected result"(boolean object) to be equal to true
/ false
.
What it means from rspec expectations' truthiness is:
expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
expect(actual).to be true # passes if actual == true
expect(actual).to be_falsy # passes if actual is falsy (nil or false)
expect(actual).to be false # passes if actual == false
expect(actual).to be_nil # passes if actual is nil
expect(actual).to_not be_nil # passes if actual is not nil
Examples -
For be true
and be false
:
it { expect(true).to be true } # passes
it { expect("string").to be true } # fails
it { expect(nil).to be true } # fails
it { expect(false).to be true } # fails
it { expect(false).to be false } # passes
it { expect("string").to be false} # fails
it { expect(nil).to be false} # fails
it { expect(true).to be false} # fails
For be_truthy
and be_falsy
:
it { expect(true).to be_truthy } # passes
it { expect("string").to be_truthy } # passes
it { expect(nil).to be_truthy } # fails
it { expect(false).to be_truthy } # fails
it { expect(false).to be_falsy } # passes
it { expect(nil).to be_falsy } # passes
it { expect("string").to be_falsy} # fails
it { expect(true).to be_falsy } # fails
You can use any other object as "expected result" at the place of "string"
except nil
/true
/false
, because they are already present in the examples shown above.
RSpec should be_true vs should == true
The convention is: expect(something).to be_true
however there is no difference when used properly. Expecting something to == true
should only be evaluated against expressions.
- See docs: https://github.com/rspec/rspec-expectations#truthiness
Rspec Tests:
describe "is the equation true? " do
let!(:add) { 5+5 == 10 }
it "be_true" do
add.should be_true #passes
end
it "expect be_true" do
expect(add).to be_true #passes
end
it "== true" do
add.should == true #passes
end
end
Update for strings:
A string is not == true
so it will fail, but it will pass as be_true
because anything except for false
or nil
is considered truthy
. However, this is NOT how you check for a string
.
See this example for how to properly check for a value in a string
:
str = "happy dance!"
For finding if text is in a string
, you would use: expect(str).to include("happy dance!")
Differences with Rspec syntax eq and to be
be_true
has been renamed to be_truthy
in the most recent versions of RSpec.
Because be_true
was removed, the spec may fail since the method is no longer there.
The solution is to update the code to use the new method. eq(true)
works, but it's slightly different: be_truthy
and be_falsey
are designed to match true/false-alike values (e.g. nil evaluates to false, but is not equal to false).
If you use eq(true)
you pretend a strict match.
be false vs be_falsey in rspec
There are lots of situations in Ruby where you get a nil
result and want to treat it like false
. For example, suppose we have a method that handles an options hash, and we want the absence of an option to be the same as the option set to false:
def verbose?(opts)
opts[:verbose]
end
opts = { verbose: false }
expect(verbose?(opts)).to be_falsey # => PASS
opts = { verbose: true }
expect(verbose?(opts)).to be_falsey # => FAIL
opts = {}
expect(verbose?(opts)).to be_falsey # => PASS
Obviously this is a simplistic example (you could argue that verbose?
should always return true
or false
), but similar scenarios are common in Ruby.
expected true to respond to true?
From rspec 3.0, be_true
is renamed to be_truthy
and be_false
to be_falsey
The behavior has not changed. So
(nil).should be_falsey
(false).should be_falsey
will pass, and
(anything other than nil or false).should be_truthy
will also pass
From the changelog 3.0.0.beta1 / 2013-11-07
Rename be_true and be_false to be_truthy and be_falsey. (Sam Phippen)
RSpec: How to test for a boolean return type?
expect(variable).to be_in([true, false])
Best Way to Test Boolean Values in Rspec
Here's the difference between "== true" and "be(true)":
describe true do
it { should be(true) }
it { should be_true }
end
describe 'true' do
it { should_not be(true) }
it { should be_true }
end
Which basically means that if you only care if a value evaluates to true then you want to use == true
rspec what is the difference between be nil and be_nil
There is no difference really, except be nil
gets defined on the fly, and be_nil
has been specifically programmed by rspec.
when you say should.be something
, rspec tries the following
[:==, :<, :<=, :>=, :>, :===].each do |operator|
define_method operator do |operand|
BeComparedTo.new(operand, operator)
end
end
Whereas, when you try should.be_nil
it just checks
object.nil?
https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/built_in/be.rb
capybara matchers always eval to TRUE in rspec. Why?
Turns out the culprit was having both "bacon" and "rspec" in the Gemfile. Capybara was being introduced to a project that utilized bacon for the test suite and examples being tried were rspec. Once bacon was removed from the bundled gems, the capybara specs ran per documentation.
Since the bacon scripts run more or less as-is under rspec, the project decision is to remove bacon and go with rspec for the test suite and make the minor tweaks to the bacon scripts to run all specs under rspec.
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,
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 untilsubject
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), saysubject!
instead ofsubject
.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) }
endThe 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 (assubject
), 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 ofsubject
/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
Related Topics
Mongoid: Find Through Array of Ids
CSV - Unquoted Fields Do Not Allow \R or \N (Line 2)
Polymorphic Association with Multiple Associations on the Same Model
How to Find a Model's Relationships
Rake Db:Migrate Error with MySQL2 Gem - Library Not Loaded: Libssl.1.0.0.Dylib
How to Get the Current Route in Rails
Help Understanding Yield and Enumerators in Ruby
How to Test for a Redirect with Rspec and Capybara
Cannot Load Such File -- 1.9/Bcrypt_Ext (Loaderror)
How to Trace and Check Dependencies in Bundled Ruby Gems
How to Ignore Multiline Comments in SASS
Nokogiri Error When Running Bundle Install
Refactoring a Large Routes.Rb File in Rails 4
Share Models Between 2 Rails API's (Separate Applications)
Stylesheet_Link_Tag :All Versus :Media =>All
What Does the Regular Expression [\W-] Mean
Setting Up Facets in Elasticsearch with Searchkick Gem in Rails 4.1