RSpec implict subject seems to fail, explicit subject works - ruby

I have a simple test that it seems to have a different behavior depending on whether I use an explicit subject or not.
context "successful validation" do
subject(:invitation) {invitations(:emmet_invite)}
after do
invitation.send_voucher
end
it "calls hotel_booked?" do
invitation.should_receive(:hotel_booked?).and_return(true) #works
end
it {should_receive(:hotel_booked?).and_return(true)} #fails
end
What's wrong here?

See this github issue in rspec-mocks: https://github.com/rspec/rspec-mocks/issues/148
Quote:
Implicit subject was added to support one line expectations, but I don't believe it was ever intended for use with mock expectations. For a mock expectation to make any sense, there has to be some code in the example after it was set, so it doesn't really make sense to use for one liners.
So basically, you can't use should_receive with an implicit subject.

You can use subject to get at the implicit subject:
it { subject.should_receive(:hotel_booked?).and_return(true) }
though this is not as easy to read as your explicit example.

Related

Can RSpec be configured to fail any example which does not contain any expectations?

I know that in RSpec you can easily mark an example as incomplete by omitting the block:
it 'is not a complete example'
But is it possible to get RSpec to fail any example which does not contain any expectations?
IE. RSpec will happily pass this example:
it 'does not have any expectations but still passes' do
end
I want to emulate the behavior of PHPUnit which fails any test not containing any assertions.
There isn't a built-in way, but it's a feature I'd like to see added at some point.
In the meantime, it's not too hard to add yourself:
http://blog.sorah.jp/2012/12/17/rspec-warn-for-no-expectations

rspec: 'should_receive' with multiple argument expectations

I have a function which receives a complex argument (an HTML string). I want to check multiple conditions about this string, i.e.:
receiver.should_receive(:post_data).with(json_content).with(id_matching(5))
Multiple with arguments doesn't work, any alternatives? I'm happy to define custom matchers if it's possible to make a compound one in some way.
Obviously I could run the same test multiple times and test different things about the result, however this is an integration test which takes several seconds to run, so I don't want to make it even slower.
Thanks
EDIT:
At time of writing, the accepted answer (use a custom matcher with custom description), appears to be the best option. However it isn't perfect, ideally with would support a concept of 'this was an item of the expected type, but wasn't the one we expected', instead of a pure binary match.
Maybe you don't even need a custom matcher and the block form is sufficient for you.
receiver.should_receive(:post_data) do |*args|
json_content = args.first
json_content.should_not be_empty
json_content.should include "some string"
end
See RSpec Mocks documentation, section Arbitrary Handling
You need to provide a custom matcher, but you can readily define your error reporting so that you can give specifics about what failed and why. See https://github.com/dchelimsky/rspec/wiki/Custom-Matchers .
In particular, the custom matcher would be supplied as the argument to with, as mentioned in the last sentence of the first paragraph of the "Argument Matchers" section of https://github.com/rspec/rspec-mocks.
As for error reporting, there are no custom failure methods that apply to this use case, but the description method of the custom matcher is used generate the string shown as the "expected" value and, though not its purpose, can be defined to output anything you want regarding the failed match.

When to use RSpec let()?

I tend to use before blocks to set instance variables. I then use those variables across my examples. I recently came upon let(). According to RSpec docs, it is used to
... to define a memoized helper method. The value will be cached across multiple calls in the same example but not across examples.
How is this different from using instance variables in before blocks? And also when should you use let() vs before()?
I always prefer let to an instance variable for a couple of reasons:
Instance variables spring into existence when referenced. This means that if you fat finger the spelling of the instance variable, a new one will be created and initialized to nil, which can lead to subtle bugs and false positives. Since let creates a method, you'll get a NameError when you misspell it, which I find preferable. It makes it easier to refactor specs, too.
A before(:each) hook will run before each example, even if the example doesn't use any of the instance variables defined in the hook. This isn't usually a big deal, but if the setup of the instance variable takes a long time, then you're wasting cycles. For the method defined by let, the initialization code only runs if the example calls it.
You can refactor from a local variable in an example directly into a let without changing the
referencing syntax in the example. If you refactor to an instance variable, you have to change
how you reference the object in the example (e.g. add an #).
This is a bit subjective, but as Mike Lewis pointed out, I think it makes the spec easier to read. I like the organization of defining all my dependent objects with let and keeping my it block nice and short.
A related link can be found here: http://www.betterspecs.org/#let
The difference between using instances variables and let() is that let() is lazy-evaluated. This means that let() is not evaluated until the method that it defines is run for the first time.
The difference between before and let is that let() gives you a nice way of defining a group of variables in a 'cascading' style. By doing this, the spec looks a little better by simplifying the code.
I have completely replaced all uses of instance variables in my rspec tests to use let(). I've written a quickie example for a friend who used it to teach a small Rspec class: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html
As some of the other answers here says, let() is lazy evaluated so it will only load the ones that require loading. It DRYs up the spec and make it more readable. I've in fact ported the Rspec let() code to use in my controllers, in the style of inherited_resource gem. http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html
Along with lazy evaluation, the other advantage is that, combined with ActiveSupport::Concern, and the load-everything-in spec/support/ behavior, you can create your very own spec mini-DSL specific to your application. I've written ones for testing against Rack and RESTful resources.
The strategy I use is Factory-everything (via Machinist+Forgery/Faker). However, it is possible to use it in combination with before(:each) blocks to preload factories for an entire set of example groups, allowing the specs to run faster: http://makandra.com/notes/770-taking-advantage-of-rspec-s-let-in-before-blocks
It is important to keep in mind that let is lazy evaluated and not putting side-effect methods in it otherwise you would not be able to change from let to before(:each) easily.
You can use let! instead of let so that it is evaluated before each scenario.
In general, let() is a nicer syntax, and it saves you typing #name symbols all over the place. But, caveat emptor! I have found let() also introduces subtle bugs (or at least head scratching) because the variable doesn't really exist until you try to use it... Tell tale sign: if adding a puts after the let() to see that the variable is correct allows a spec to pass, but without the puts the spec fails -- you have found this subtlety.
I have also found that let() doesn't seem to cache in all circumstances! I wrote it up in my blog: http://technicaldebt.com/?p=1242
Maybe it is just me?
Dissenting voice here: after 5 years of rspec I don't like let very much.
1. Lazy evaluation often makes test setup confusing
It becomes difficult to reason about setup when some things that have been declared in setup are not actually affecting state, while others are.
Eventually, out of frustration someone just changes let to let! (same thing without lazy evaluation) in order to get their spec working. If this works out for them, a new habit is born: when a new spec is added to an older suite and it doesn't work, the first thing the writer tries is to add bangs to random let calls.
Pretty soon all the performance benefits are gone.
2. Special syntax is unusual to non-rspec users
I would rather teach Ruby to my team than the tricks of rspec. Instance variables or method calls are useful everywhere in this project and others, let syntax will only be useful in rspec.
3. The "benefits" allow us to easily ignore good design changes
let() is good for expensive dependencies that we don't want to create over and over.
It also pairs well with subject, allowing you to dry up repeated calls to multi-argument methods
Expensive dependencies repeated in many times, and methods with big signatures are both points where we could make the code better:
maybe I can introduce a new abstraction that isolates a dependency from the rest of my code (which would mean fewer tests need it)
maybe the code under test is doing too much
maybe I need to inject smarter objects instead of a long list of primitives
maybe I have a violation of tell-don't-ask
maybe the expensive code can be made faster (rarer - beware of premature optimisation here)
In all these cases, I can address the symptom of difficult tests with a soothing balm of rspec magic, or I can try address the cause. I feel like I spent way too much of the last few years on the former and now I want some better code.
To answer the original question: I would prefer not to, but I do still use let. I mostly use it to fit in with the style of the rest of the team (it seems like most Rails programmers in the world are now deep into their rspec magic so that is very often). Sometimes I use it when I'm adding a test to some code that I don't have control of, or don't have time to refactor to a better abstraction: i.e. when the only option is the painkiller.
let is functional as its essentially a Proc. Also its cached.
One gotcha I found right away with let... In a Spec block that is evaluating a change.
let(:object) {FactoryGirl.create :object}
expect {
post :destroy, id: review.id
}.to change(Object, :count).by(-1)
You'll need to be sure to call let outside of your expect block. i.e. you're calling FactoryGirl.create in your let block. I usually do this by verifying the object is persisted.
object.persisted?.should eq true
Otherwise when the let block is called the first time a change in the database will actually happen due to the lazy instantiation.
Update
Just adding a note. Be careful playing code golf or in this case rspec golf with this answer.
In this case, I just have to call some method to which the object responds. So I invoke the _.persisted?_ method on the object as its truthy. All I'm trying to do is instantiate the object. You could call empty? or nil? too. The point isn't the test but bringing the object ot life by calling it.
So you can't refactor
object.persisted?.should eq true
to be
object.should be_persisted
as the object hasn't been instantiated... its lazy. :)
Update 2
leverage the let! syntax for instant object creation, which should avoid this issue altogether. Note though it will defeat a lot of the purpose of the laziness of the non banged let.
Also in some instances you might actually want to leverage the subject syntax instead of let as it may give you additional options.
subject(:object) {FactoryGirl.create :object}
"before" by default implies before(:each). Ref The Rspec Book, copyright 2010, page 228.
before(scope = :each, options={}, &block)
I use before(:each) to seed some data for each example group without having to call the let method to create the data in the "it" block. Less code in the "it" block in this case.
I use let if I want some data in some examples but not others.
Both before and let are great for DRYing up the "it" blocks.
To avoid any confusion, "let" is not the same as before(:all). "Let" re-evaluates its method and value for each example ("it"), but caches the value across multiple calls in the same example. You can read more about it here: https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let
Note to Joseph -- if you are creating database objects in a before(:all) they won't be captured in a transaction and you're much more likely to leave cruft in your test database. Use before(:each) instead.
The other reason to use let and its lazy evaluation is so you can take a complicated object and test individual pieces by overriding lets in contexts, as in this very contrived example:
context "foo" do
let(:params) do
{ :foo => foo, :bar => "bar" }
end
let(:foo) { "foo" }
it "is set to foo" do
params[:foo].should eq("foo")
end
context "when foo is bar" do
let(:foo) { "bar" }
# NOTE we didn't have to redefine params entirely!
it "is set to bar" do
params[:foo].should eq("bar")
end
end
end
I use let to test my HTTP 404 responses in my API specs using contexts.
To create the resource, I use let!. But to store the resource identifier, I use let. Take a look how it looks like:
let!(:country) { create(:country) }
let(:country_id) { country.id }
before { get "api/countries/#{country_id}" }
it 'responds with HTTP 200' { should respond_with(200) }
context 'when the country does not exist' do
let(:country_id) { -1 }
it 'responds with HTTP 404' { should respond_with(404) }
end
That keeps the specs clean and readable.

Validate arguments in Ruby?

I wonder if one should validate that the arguments passed to a method is of a certain class.
eg.
def type(hash = {}, array = [])
# validate before
raise "first argument needs to be a hash" unless hash.class == Hash
raise "second argument needs to be an array" unless array.class == Array
# actual code
end
Is it smart to do this or is it just cumbersome and waste of time to validate all passed in arguments?
Are there circumstances when you would like to have this extra security and circumstances when you won't bother?
Share your experiences!
I wouldn't recommend this specific approach, as you fail to accommodate classes that provide hash or array semantics but are not that class. If you need this kind of validation, you're better off using respond_to? with a method name. Arrays implement the method :[], for what it's worth.
OpenStruct has hash semantics and attribute-accessor method semantics, but won't return true for the condition hash.class==Hash. It'll work just like a hash in practice, though.
To put it in perspective, even in a non-dynamic language you wouldn't want to do it this way; you'd prefer to verify that an object implements IDictionary<T>. Ruby would idiomatically prefer that, when necessary, you verify that the method exists, because if it does, the developer is probably intending their object to act alike. You can provide additional sanity with unit tests around the client code as an alternative to forcing things to be non-dynamic.
There's usually no need to validate that arguments are of a certain class. In Ruby, you're encouraged to use Duck Typing.
I have found that validating that the input parameters meet your preconditions is a very valuable practice. The stupid person that its saves you from is you. This is especially true for Ruby as it has no compile time checks. If there are some characteristics of the input of your method that you know must be true it makes senes to check them at run time and raise errors with meaningful error messages. Otherwise, the code just starts producing garbage out for garbage in and you either get a wrong answer or an exception down the line.
I think that it is unnecessary. I once read on a blog something like "If you need to protect your code from stupid people, ruby isn't the language for you."
If you want compiler/runtime-enforced code contracts, then Ruby isn't for you. If you want a malleable language and easy testability, then Ruby is.

Is it idiomatic Ruby to add an assert( ) method to Ruby's Kernel class?

I'm expanding my Ruby understanding by coding an equivalent of Kent Beck's xUnit in Ruby. Python (which Kent writes in) has an assert() method in the language which is used extensively. Ruby does not. I think it should be easy to add this but is Kernel the right place to put it?
BTW, I know of the existence of the various Unit frameworks in Ruby - this is an exercise to learn the Ruby idioms, rather than to "get something done".
No it's not a best practice. The best analogy to assert() in Ruby is just raising
raise "This is wrong" unless expr
and you can implement your own exceptions if you want to provide for more specific exception handling
I think it is totally valid to use asserts in Ruby. But you are mentioning two different things:
xUnit frameworks use assert methods for checking your tests expectations. They are intended to be used in your test code, not in your application code.
Some languages like C, Java or Python, include an assert construction intended to be used inside the code of your programs, to check assumptions you make about their integrity. These checks are built inside the code itself. They are not a test-time utility, but a development-time one.
I recently wrote solid_assert: a little Ruby library implementing a Ruby assertion utility and also a post in my blog explaining its motivation. It lets you write expressions in the form:
assert some_string != "some value"
assert clients.empty?, "Isn't the clients list empty?"
invariant "Lists with different sizes?" do
one_variable = calculate_some_value
other_variable = calculate_some_other_value
one_variable > other_variable
end
And they can be deactivated, so assert and invariant get evaluated as empty statements. This let you avoid performance problems in production. But note that The Pragmatic Programmer: from journeyman to master recommends against deactivating them. You should only deactivate them if they really affect the performance.
Regarding the answer saying that the idiomatic Ruby way is using a normal raise statement, I think it lacks expressivity. One of the golden rules of assertive programming is not using assertions for normal exception handling. They are two completely different things. If you use the same syntax for the two of them, I think your code will be more obscure. And of course you lose the capability of deactivating them.
Some widely-regarded books that dedicate whole sections to assertions and recommend their use:
The Pragmatic Programmer: from Journeyman to Master by Andrew Hunt and David Thomas
Code Complete: A Practical Handbook of Software Construction by Steve McConnell
Writing Solid Code by Steve Maguire
Programming with
assertions
is an article that illustrates well what assertive programming is about and
when to use it (it is based in Java, but the concepts apply to any
language).
What's your reason for adding the assert method to the Kernel module? Why not just use another module called Assertions or something?
Like this:
module Assertions
def assert(param)
# do something with param
end
# define more assertions here
end
If you really need your assertions to be available everywhere do something like this:
class Object
include Assertions
end
Disclaimer: I didn't test the code but in principle I would do it like this.
It's not especially idiomatic, but I think it's a good idea. Especially if done like this:
def assert(msg=nil)
if DEBUG
raise msg || "Assertion failed!" unless yield
end
end
That way there's no impact if you decide not to run with DEBUG (or some other convenient switch, I've used Kernel.do_assert in the past) set.
My understanding is that you're writing your own testing suite as a way of becoming more familiar with Ruby. So while Test::Unit might be useful as a guide, it's probably not what you're looking for (because it's already done the job).
That said, python's assert is (to me, at least), more analogous to C's assert(3). It's not specifically designed for unit-tests, rather to catch cases where "this should never happen".
How Ruby's built-in unit tests tend to view the problem, then, is that each individual test case class is a subclass of TestCase, and that includes an "assert" statement which checks the validity of what was passed to it and records it for reporting.

Resources