Is there a way to provide better rspec test descriptions - ruby-on-rails

I have been using rspec for a little while and recently switched style from
it "should do something cool" do
#something.should work
end
to the more concise
subject(#something)
it {should work}
Whilst I much prefer the more concise style when writing the tests and for viewing them in code, I miss being able to specify the description for each test, particularly since the equality messages just display the values that are being tested. So in the example above, assuming the test passes, with the first style, I would get a message saying 'it should do something cool', whereas the second will just say that it has worked.
Does anyone know of a way to do this? Cheers

I think you got the wrong concept of what the specify/subject blocks do. They are not meant to be a complete replacement for the more verbose syntax you switched from, but should be used when there is no need for a desciption.
So if you want a desciption, just use
it "should do something cool" do
#something.should work
end
Also, I personally don't think that the specify/subject is more concise. For me it's a step away from the more DSL-like way specifications created with rspec read, but that might be a matter of personal preference.

Related

How does Rspec 'let' helper work with ActiveRecord?

It said here https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/helper-methods/let-and-let what variable defined by let is changing across examples.
I've made the same simple test as in the docs but with the AR model:
RSpec.describe Contact, type: :model do
let(:contact) { FactoryGirl.create(:contact) }
it "cached in the same example" do
a = contact
b = contact
expect(a).to eq(b)
expect(Contact.count).to eq(1)
end
it "not cached across examples" do
a = contact
expect(Contact.count).to eq(2)
end
end
First example passed, but second failed (expected 2, got 1). So contacts table is empty again before second example, inspite of docs.
I was using let and was sure it have the same value in each it block, and my test prove it. So suppose I misunderstand docs. Please explain.
P.S. I use DatabaseCleaner
P.P.S I turn it off. Nothing changed.
EDIT
I turned off DatabaseCleaner and transational fixtures and test pass.
As I can understand (new to programming), let is evaluated once for each it block. If I have three examples each calling on contact variable, my test db will grow to three records at the end (I've tested and so it does).
And for right test behevior I should use DatabaseCleaner.
P.S. I use DatabaseCleaner
That's why your database is empty in the second example. Has nothing to do with let.
The behaviour you have shown is the correct behaviour. No example should be dependant on another example in setting up the correct environment! If you did rely on caching then you are just asking for trouble later down the line.
The example in that document is just trying to prove a point about caching using global variables - it's a completely different scenario to unit testing a Rails application - it is not good practice to be reliant on previous examples to having set something up.
Lets, for example, assume you then write 10 other tests that follow on from this, all of which rely on the fact that the previous examples have created objects. Then at some point in the future you delete one of those examples ... BOOM! every test after that will suddenly fail.
Each test should be able to be tested in isolation from any other test!

Multiple should statements in one rspec it clause - bad idea?

Here's my rspec test:
it "can release an idea" do
james.claim(si_title)
james.release(si_title)
james.ideas.size.should eq 0
si_title.status.should eq "available"
end
Are the two should lines at the end a really bad idea? I read somewhere that you should only test for one thing per it block, but it seems silly to make a whole test just to make sure that the title status changes (the same function does both things in my code).
My interpretation of this isn't so much that there should be precisely one assertion / call to should per spec, but that there should only be one bit of behaviour tested per spec, so for example
it 'should do foo and bar' do
subject.do_foo.should be_true
subject.do_bar.should be_true
end
is bad - you're speccing 2 different behaviours at the same time.
on the other hand if your 2 assertions are just verifying different aspects of one thing then I'm ok with that, for example
it 'should return a prime integer' do
result = subject.do_x
result.should be_a(Integer)
result.foo.should be_prime
end
For me it wouldn't make too much sense to have one spec that checks that it returns an integer and a separate one that it returns a prime.
Of course in this case, the be_prime matcher could easily do both of these checks - perhaps a good rule of thumb is that multiple assertions are ok if you could sensibly reduce them to 1 with a custom matcher (whether actually doing this is actually worthwhile probably depends on your situation)
In your particular case, it could be argued that there are 2 behaviours at play - one is changing the status and other is mutating the ideas collection. I would reword your specs to say what the release method should do -
it 'should change the status to available'
it 'should remove the idea from the claimants ideas'
At the moment those things always happen simultaneously but I would argue they are separate behaviours - you could easily imagine a system where multiple people can claim/release an idea and the status only change when the last person releases the idea.
I have the same problem...
It ought to be one should per it (my boss says) by it makes testing time consuming and, as you said, silly.
Tests require good sense and flexibility or they might end up enslaving you.
Anyway, I agree with your test.
My policy is to always consider multiple assertions to be a sign of a potential problem and worth a second thought, but not necessarily wrong. Probably 1/3 of the specs I write end up having multiple assertions in them for one reason or another.
One of the problems with having multiple assertions is that when one fails, you can't see if the other one passed or not. This can sometimes be worked around by building an array of results and asserting the value of the array.
In the case you are asking about, I don't think that multiple assertions are a problem, but I do see something else that seems an issue to me. It looks like your spec may be overly coupled.
I think that you're trying to assert the behavior of whatever kind of a thing james is, but the test is also depending on behavior of si_title in order to tell us what was done to it (by way of its eventual #status value). What I would usually do instead is make si_title a test-double and use #should_receive to directly specify the messages that it should expect.
I think Frederick Cheung gave a very good answer (+1) especially the why, but I'd also like to give a comparison bit of code for you to look at which uses the its, lets, before and context syntax:
context "Given a si_title" do
let(:james) { James.new } # or whatever
before do
james.claim(si_title)
james.release(si_title)
end
context "That is valid" do
let(:si_title) { Si_title.new } # or whatever
describe "James' ideas" do
subject { james.ideas }
its(:size) { should == 0 }
its(:whatever) { should == "Whatever" }
end
describe "Si title" do
subject { si_title }
its(:status) { should == "available" }
end
end
context "That is invalid" do
# stuff here
end
end
I would even go further and make the expected values lets and then make the examples shared_examples, so that they could then be use to check the different aspects (null arguments, invalid arguments, an incorrect object… ), but I believe that this is a far better way to spec your intentions and still cut down on any repetition. Taking the example from Frederick's answer:
it 'should return a prime integer' do
result = subject.do_x
result.should be_a(Integer)
result.foo.should be_prime
end
Using RSpec's syntax to full effect you get this:
let(:result) { 1 }
subject{ result }
its(:do_x) { should be_a(Integer) }
its(:foo) { should be_prime }
Which means you can check multiple aspects of a subject.

Is it the right way to output more information about tests that are going to be run?

I am using Ruby on Rails 3.2.2 and rspec-rails-2.8.1. I would like to output more information about tests that are going to be run, for example, this way:
# file_name.html.erb
...
# General idea
expected_value = ...
it "... #{expected_value}" do
...
end
# Usage that I am trying to implement
expected_page_title =
I18n.translate(
'page_title_html'
:user => #user.firstname
)
it "displays the #{expected_page_title} page title" do
view.content_for(:page_title).should have_content(expected_page_title)
end
Note: "Outputs" are intended to be those that are output when you run the rspec . --format documentation command line in the Terminal window.
Is it a right way to test?
Related questions:
How to use an instance variable throughout an Example Group, even if it is outside a Example?
Your question is going to solicit some opinions, but I'll try and justify mine with some examples.
Short answer: No, this isn't how you should be writing RSpec (or any test) descriptions. It's unconventional and doesn't add much value for the extra code.
Long answer: RSpec is a BDD (behavior driven development) tool that was designed to help describe the behavior and intent of your code at the same time as writing automated tests. When you think about the behavior of your code, does adding the expected result to the test description really add much value? If so, maybe you should rethink what you are testing.
For example, say you have a User class and you want to test a method that concatenates a user's first and last name:
describe User do
expected_full_name = 'Software Guy'
subject { User.new(first: 'Software', last: 'Guy') }
it 'should have the full name #{expected_full_name}' do
subject.full_name.should == 'Software Guy'
end
end
VS
describe User do
subject { User.new(first: 'Software', last: 'Guy') }
it 'should have a full name based on the first and last names' do
subject.full_name.should == 'Software Guy'
end
end
In the first test, what does having the expected result in the description really buy you? Does it tell you anything about the expected behavior of a user? Not really.
Take your example. If I was coming along to your project and saw a test description like that, I would be confused because it doesn't really tell me what is being tested. I would still need to look at the code to understand what is going on. Compare these two examples:
it "displays the #{expected_page_title} page title" do
view.content_for(:page_title).should have_content(expected_page_title)
end
Which would give you something in the console like:
"displays the My Awesome Title page title"
Compare that to:
it "should translate the page title" do
view.content_for(:page_title).should have_content(expected_page_title)
end
Which would be exactly the same in the console as it is in the test:
"should translate the page title"
Your obviously free to choose whichever one you want, but I am speaking from a few years of testing experience and highly recommend you don't do this.

Cucumber, How to check if parent has a certain class?

I am using cucumber with rails, and I'm checking if the parent of an element has a certain class. I found this code, but it doesn't work. I'm using Capybara by the way. Thanks in advance!
Then the element "([^"]*)" with parent "([^"]*)" should have class "([^"]*)" do |element_id,parent,css_class|
response.should have_selector "#{parent} .#{css_class}" do |matches|
matches.should have_selector element_id
end
end
Technically, this isn't really the way you should be testing with cucumber. Cucumber is for testing behaviour, not checking the DOM - though of course it can be used in that way.
If you're really wanting to write something like this, you could make that much simpler:
Then /^the element "([^"]*)" with parent "([^"]*)" should have class "([^"]*)"$/ do |element_id,parent,css_class|
page.should have_css("#{parent}.#{css_class} #{element_id}")
end
This sucks on many levels, though. Not only are you checking the DOM directly from the step, which makes for very unreadable features, but you're mixing passing in element names, class names and element IDs - each with a slightly different style... In this step, element can be any type of selector whereas parent and css_class are much more fixed - they must always 'fit' into the selector string or it won't find anything.
I haven't really explained that very well, but in a nutshell, you should consider what you're actually trying to test and think whether it can be renamed to something more useful and re-usable. Would you ever be able to re-use that step without looking at it's implementation to figure out what goes where?
Also, with more expressive naming, the test instantly becomes more useful later down the line. For example, the step Then I should see the current list is active is much more readable and expressive than Then the element "li.active" with parent "ul" should have class "active-list". Be specific in the step definition implementation, not your features!
Have a read of this blog post - http://elabs.se/blog/15-you-re-cuking-it-wrong - it should give you a good idea of how to write better steps.

Is refactoring of tests/spec a good idea?

When I see in my test code specs like this for every controller:
it "#new displays input controls for topic title and keywords" do
ensure_im_signed_in
get :new
assert_response :success
assert_select "input#topic_title"
assert_select "input#topic_keywords_input"
assert assigns :topic
end
I want to refactor it and replace with some one-liner like this:
its_new_action_displays_input_form :topic, %w(input#topic_title input#topic_keywords_input)
and implementation:
def its_new_Action_displays_input_form field, inputs
it "#new displays input controls for #{inputs.join ", "}" do
ensure_im_signed_in
get :new
assert_response :success
for css in inputs
assert_select css
end
assert assigns field
end
end
What are the advantages of either keeping verbose version or refactoring to terser version?
I see only problem with refactored version is that RSpec does not show backtrace for the failed test.
The biggest question we always ask is "who is testing the tests?" You should definately go with refactoring your unit tests where ever you are and at whatever stage you are as it helps reduce the complexity. Reducing the complexity within your unit tests will yield the same benefits as reducing the complexity within your code base. I can see very little reason not to do it and many reasons to do it.
My very first job out of college was updating a set of unit tests. They were about 7 years old, with many deprecated methods being used. It took me eight months (there were a lot of them!) and I still didn't finish the job. However, I did manage to reduce the file size to about 1/3rd the original size and greatly simplify the work by refactoring much of it, and that helped speed up the work.
So, I would definitely encourage refactoring!
You refactor the spec because of the same reasons that you refactor everything else: It will make your application – be it you production app or tests suit – runs faster.
Also, refactoring a piece of code makes you think about the intention that you had to writing such a thing. This increases your comprehension of the problem and will make you more capable of solving other problems in the same application.
Yet I would strongly disagree about your refactoring. You see, when you are watching a spec you want to see fast and clear what are you testing. If you move the asserts to other place, you are hitting your self, because you need to go and find where did you put that thing.
I suggest you that never move the asserts out of your spec. Even if they are repeated. That's fine. It remarks the intention, so you never forget what you want to test.
Instead, focus your refactoring on boiler code.
For example, you are testing a form and to get to that page you need to click many links. A first approach would be to put everything in the spec. A lot of click_link.
I refactoring of such code will be put the whole bunch of click_link to a before(:each). Use Context as well, to clarify.
Like this:
feature "Course" do
context "Loged in" do
before(:each) do
school = School.make!
switch_to_subdomain(school)
end
context "In the new course form" do
before(:each) do
click_link("Asignaturas")
click_link("Nueva asignatura")
end
scenario "New course" do
fill_in(:name, :with => "Matematicas")
click_button("Create Course")
page.has_content?("Asignatura creada").should == true
dbSchool = School.find(school.id)
dbSchool.courses.count.should == 1
end
scenario "New course" do
fill_in(:name, :with => "")
click_button("Create Course")
page.has_content?("Asignatura creada").should == false
dbSchool = School.find(school.id)
dbSchool.courses.count.should == 0
end
end
end
end
You see? The boiler code is out of the specific spec. But let enough on the spec so that 7 month after that you can understand what you are testing. Never take away the asserts.
So my answer: Refactor Spec is not only a good idea, but a necessity. But don't mix refactoring with hiding code.
You should indeed refactor your tests to remove duplication. However, there are likely ways to do it that that keep the failure backtrace.
Rather than pulling everything out to a single method, you might do well to have a shared
before(:each) section for the common setup, and write your own matchers and/or assertions to combine the checks.

Resources