Using RSpec, are constants within describes a big no-no? - ruby

I just spent ages trying to figure out why my specs were passing in isolation, but when running the controller and lib tests together, some specs were mysteriously failing. The culprit was this:
In one spec:
describe SomeThing do
CONSTANT_VALUE = "a value"
# ... examples etc ...
end
And in another:
describe AnotherThing do
CONSTANT_VALUE = "a different value"
# ... the rest is history
end
The values I'd assigned to these constants was leaking between my specs and causing unexpected behaviour. Am I supposed to use a let block for defining constants etc? Or something else?

Yes, let is the answer here.

Related

Encountering: syntax error, unexpected tIDENTIFIER, expecting keyword_end

I'm creating a directed_graph class in Ruby to practice using RSpec. I keep getting the above error (at line 13, which is the line below with "eql(0)" on it).
I don't really understand the error, especially since this RSpec code looks very similar to other RSpec code I've written for other projects that works.
require "directed_graph"
include directed_graph
describe directed_graph do
describe ".vertices" do
context "given an empty graph" do
it "returns an empty hash" do
g = directed_graph.new()
expect(g.vertices().length()).to() eql(0)
end
end
end
end
EDIT: I believe the problem was (1) directed_graph was a class, and classes must start with uppercase letters (so I renamed is DirectedGraph), and (2) you're not supposed to write "include" for classes.
I fixed those two, and my code seems to be runnign fine for now. I'm going to leave this up here in case I missed something big.
I believe the code should look like this:
require "directed_graph"
include DirectedGraph
describe DirectedGraph do
describe ".vertices" do
context "given an empty graph" do
it "returns an empty hash" do
expect(directed_graph.new.vertices.length).to eql(0)
end
end
end
end
Let me explain why. First include usually includes Classes/Modules. Classes and modules in ruby are denoted with Capital Letters for each part of their name (also know as UpperCamelCase). When you describe a class in in rspec, you should also use UpperCamelCase. I also cleaned up the code a little bit to make it easier to read. You don't always need the () to denote a function. It is implied. But sometimes you do need it, for example with the expect function.

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

http://betterspecs.org/#subject has some info about subject and let. However, I am still unclear on the difference between them. Furthermore, the SO post What is the argument against using before, let and subject in RSpec tests? said it is better to not use either subject or let. Where shall I go? I am so confused.
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 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.
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
Subject and let are just tools to help you tidy up and speed up your tests. People in the rspec community do use them so i wouldn't worry about whether it's ok to use them or not. They can be used similarly but serve slightly different purposes
Subject allows you to declare a test subject, and then reuse it for any number of following test cases afterward. This reduces code repetition (DRYing up your code)
Let is an alternative to before: each blocks, which assign test data to instance variables. Let gives you a couple of advantages. First, it caches the value without assigning it to an instance variable. Second, it is lazily evaluated, which means that it doesn't get evaluated until a spec calls for it. Thus let helps you speed up your tests. I also think let is easier to read
subject is what is under test, usually an instance or a class. let is for assigning variables in your tests, which are evaluated lazily vs. using instance variables. There are some nice examples in this thread.
https://github.com/reachlocal/rspec-style-guide/issues/6

Defining and using constants inside Rspec's hooks

I am kinda of new to Rspec and I am writing some test examples when a question came up.
First, I was using global variables to define three constant values I was going to use in a lot of tests. For example:
$message = 'this is a test'
However, I notice that this global was being carried over to other test files and causing issues. Ok... decided then to avoid the globals as this can cause a lot of pain in the future in case my test file number grows.
I then went for the let() block. Example:
let(:message) { 'this is a test' }
Now the problem was that this variable could not be used inside before and after hooks. Ok...
My last try was using constants inside the contexts/describes, like this:
self::MESSAGE = 'this is a test'
However, just like let's this cannot be used inside the hooks as they are not either classes or modules.
So, I am stuck ...
How do you guys deal with that? Will I need to create instance variables for that? Is that any other alternative I can use?
Thank you very much.
You have one other option for defining data -- instance variables.
before do
#data = [ {one: 1, two: 2},
{one: 2, two: 3} ]
end
You can use #data in all the tests within the scope of the before. This is pretty much the same as let for the purposes of just setting up static data, but it might be a better fit for you -- without seeing your code it's hard to say.

Ruby code blocks and Chef

I am an extremely new person to Ruby and Chef. I have been trying to wrap my head around the syntax and do some research, but I am sure as you all know unless one knows the terminology, it is hard to find what you are looking for.
I have read up on Ruby code blocks, but the Chef code blocks still confuse me. I see something like this for example:
log "a debug string" do
level :debug
end
Which adds "a debug string" to the log. From what I have seen though, it seems to me like it should be represented as:
log do |message|
#some logic
end
Chef refers to these as resources. Can someone please help explain the syntax difference and give me some terminology from which I can start to educate myself with?
If you come from another language (not Ruby), this syntax might seem very strange. Let's break down things.
When calling a method with parameters, in most cases the parentheses are optional:
foo(bar) is equivalent to foo bar
foo(bar, baz) is equivalent to foo bar, baz
A Ruby block of code can be wrapped in curly braces ({}) or inside a do..end block and can be passed to a method as its last parameters (but note that there's no comma and if you're using parentheses it goes after them. Some examples:
foo(bar) { # code here }
foo(bar) do
# code here
end
foo bar do
# code here
end
foo do
# code here
end
In some cases, code blocks can receive parameters, but in Chef the resources' blocks never do. Just for reference, the syntax for that is:
foo(bar) do |baz, qux|
baz + qux
end
Specifically about Chef resources, their syntax is usually:
resource_type(name) do
attribute1 value1
attribute2 value2
end
This means that, when you say:
log "a debug string" do
level :debug
end
you're actually creating a log resource whose name attribute is set to "a debug string". It can later be referred to (in other resources, for example) using log[a debug string].
AFAIK, the name attribute is mandatory for every Chef resource type as it's what makes it unique, and allows you to, among other things, call actions on it after it has been declared.
Side note: The ruby block is usually optional for a Chef resource. If you do something like:
directory "/some/path"
Chef will compile that resource using its default attributes (among which is action :create), and try to create the named directory using those.
The do ... end here is not a usual ruby block statement.
It's a implementation of DSL (Domain Specific Language).
Here's a nice explanation [1]:
there is the concept of an internal DSL, which uses the syntax of an
exіsting language, a host language, such as Ruby. The means of the
language are used to build constructs resembling a distinct language.
The, already mentioned, Rake uses this to make code like this
possible:
task :codeGen do
# do the code generation
end
Hope that answer your question.
[1] : http://www.infoq.com/news/2007/06/dsl-or-not

RSpec -- test if block called with block defined in before

I recently asked how to test in RSpec if a block was called and the answers to that question seem to work in a simple case. The problem is when the initialization with the block is more complex. Then it is done in before and reused by a number of different tests in the context, among them the one testing if the block was evaluated. See the example:
context "the node definition using block of code" do
before do
#n=node do
# this block should be called
end
# some more complex setup concerning #n
end
it "should call the block" do
# how to test it?
end
# here a bunch of other tests using #n
end
In this case the solution with side effect changing value of a local variable does not work. Raising an exception from the block is useless since the whole statement must be properly evaluated to be used by the other tests.
Obviously I could do the tests separately, but it seems to stink, since I must copy-paste the initialization part and since the was-the-block-called test inherently belongs to this very context.
How to test if the block was evaluated in such a case?
Explanation for question asked by #zetetic below.
The context is that I'm implementing a kind of DSL, with nodes defined by their parameters and blocks of code (that can define something else in the scope of node). Since the things defined by the node's block can be pretty generic, at least for the first attempt I just need to be sure the block is evaluated and that what a user provides there will be considered. For now does not matter what it is.
Probably I should refactor my tests now and using mocks make them test behaviors rather then implementation. However it will be a little bit tricky, for the sake of some mixins and dynamic handling of messages sent to objects. For now the cincept of such tests is a little bit fuzzy in my head ;-)
Anyway your answers and comments helped me to better understand how RSpec works and explained why what I'm trying to do looks as if it did not fit to the RSpec.
Try something like this (untested by me):
context "the node definition using block of code" do
let(:node){
node = Node.new "arg1", "arg2", node_block
# more complex stuff here
node
}
context "checking the block is called" do
let(:node_block) {
double = double("node_block")
double.should_receive("some kind of arg").and_return("something")
# this will now cause a fail if it isn't called
double
}
it "should call the block" do
node.blah()
end
end
let(:node_block) {
# some real code
}
subject { node.blah() }
it { should == 2 }
# ...
end
So that's a very shaky piece of code (you'll have to fill in the gaps as you didn't give very much to go on, and let is obviously a lambda too, which could mean you've got to play around with it a bit) that uses let and a double to check it's called, and avoids using before, which is really for side effects not setting up variables for use in the specs.
#zetetic makes a very insightful comment that you're not testing behaviour here. I'm not against using rspec for doing more unit test style stuff (guidelines are made to be broken), but you might ask how later tests will pass when using a real block of code if that block isn't being called? In a way, I'm not even sure you need to check the block is called, but only you know.

Resources