rspec nested context strings concatenation - ruby-on-rails

in my rspec tests i have quite a few deep nested contexts like these:
describe "#mymethod"
it_behaves_like "specific object"
end
shared_examples_for "specific object" do
context "when it receives proper parameters" do
context "when file is in a queue" do
it "behaves as i want it to" do
#...
end
end
end
as a result when i run my rspec tests i get results like those (or more complex) in sections "Pending"/"Failures"/"Failed examples":
mymethod behaves like specific object when it receives proper parameters when file is in a queue behaves as i want it to
those context strings sometimes tend to be unreadable for the lack of separators.
how can i monkey patch rspec to add them (f.ex. "|") automatically (or is there another preferred solution)? where do descriptor strings get concatenated in rspec?

RSpec has the notion of a separator hard-coded. Unfortunately, it is not configurable right now, but I guess the maintainers would consider a patch for review.

Related

Expect multiple not_to change expectations in rspec

I'm trying to make sure certain data has remain unchanged by a single action:
expect {
# running migration and user reload here
}.not_to change(user, :avatar_url).from(sample_avatar_url).and change(user, :old_avatar).from(nil)
sample_avatar_url is a string defined in the beginning of the spec file.
Basically, I want to check whether the avatar_url and old_avatar remain untouched by what's happening in the expect block.
The output from the above code is:
expect(...).not_to matcher.and matcher is not supported, since it creates a bit of an ambiguity. Instead, define negated versions of whatever matchers you wish to negate with RSpec::Matchers.define_negated_matcher and use expect(...).to matcher.and matcher.
This doesn't work because it's not clear reading whether thats supposed to mean not change the first and not change the second, or not change the first but change the second. You have a couple of options to get around this
Since you're checking static values just don't use change
..run migration and user reload..
expect(user.avatar_url).to eq(sample_avatar_url)
expect(user.old_avatar).to eq nil
or use define_negated_matcher to create a not_change matcher
RSpec::Matchers.define_negated_matcher :not_change, :change
expect {
# running migration and user reload here
}.to not_change(user, :avatar_url).from(sample_avatar_url).and not_change(user, :old_avatar).from(nil)
As stated in Thomas answer, it does not work because it's not clear reading and you can create a negated matcher. Another option is to use the saharspec gem and its dont matcher negation.
Here is an extract from the project's README:
Another (experimental) attempt to get rid of define_negated_matcher. dont is not 100% grammatically correct, yet short and readable enought. It just negates attached matcher.
# before
RSpec.define_negated_matcher :not_change, :change
it { expect { code }.to do_stuff.and not_change(obj, :attr) }
# after: no `define_negated_matcher` needed
require 'saharspec/matchers/dont'
it { expect { code }.to do_stuff.and dont.change(obj, :attr) }
So you can write your expectation without creating a negated matcher like this:
expect {
# running migration and user reload here
}.to dont.change(user, :avatar_url).from(sample_avatar_url).and
dont.change(user, :old_avatar).from(nil)
i know it is long ago but i had a similar problem and did something like this:
expect {
# running migration and user reload here
}.not_to change { user.slice(:avatar_url, :old_avatar) }

Test parameter vs multiple regexp n RSpec

I have a method which handle failure on some api calls. I wrote tests for it:
it 'logs the error' do
expect(Rails.logger).to receive(:error).with(/Failed API call/i)
expect(Rails.logger).to receive(:error).with(/#{error_type}/)
expect(Rails.logger).to receive(:error).with(/#{server_error}/)
subject
end
but to make it work I would need to make 3 api calls or split it to 3 test cases. I don't like both of the solutions. I think the best one would be to combine 3 regexp into single expectation.
Is it possible to put multiple Regexps on single parameter in one test case?
You could combine all these regexp into one (using regexp's AND operator).
let(:expected_log_message) do
/(?=.*Failed API call)(?=.*#{error_type})(?=.*#{server_error})/i
end
this regexp will test string if it matches all of above.
Then inside a test case:
it 'logs the error' do
expect(Rails.logger).to receive(:error).with(expected_log_message)
subject
end

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.

During TDD, should I create tests for custom validations? Or I should test the validity of the entire object?

I'm very new on TDD and unit-testing, and I'm having quite a lot of doubts about the correct approach I should take during the tests of the custom model validations.
Suppose I have a custom validation:
User < ActiveRecord::Base
validate :weird_validation
def weird_validation
# Validate the weird attribute
end
end
Should I take this approach:
context "validation"
it "pass the validation with weird stuff" do
user = User.new weird: "something weird"
user.should be_valid
end
it "should't pass the validation with normal stuff" do
user = User.new weird: "something"
user.should_not be_valid
user.errors[:weird].size.should eq 1
end
end
Or this one:
context "#weird_validation" do
it "should not add an error if weird is weird" do
user = User.new
user.stub(:weird){"something weird"}
user.errors.should_not_receive :add
user.weird_validation.should eq true
end
it "should add an error if weird is not weird" do
user = User.new
user.stub(:weird){"something"}
user.errors.should_receive(:add).with(:weird, anything())
user.weird_validation.should eq false
end
end
So IMHO
The first approach
Pros
It test behaviour
Easy refactoring
Cons
Dependable of other methods
Something unrelated could make the test fail
The second approach
Pros
It doesn't relay on anything else, since everything else is stubbed
It's very specific of all the things the code should do
Cons
It's very specific of all the things the code should do
Refactoring the validations could potentially break the test
I personally think the correct approach should be the first one, but I can't avoid to think that I'm relying too much in other methods rather than the one I want to test, and if the test fails it may be because of any method withing the model. For example, it would not validate if the validation of other attribute failed.
Using the second approach I'm practically writing the code twice, which seems like a waste of time and effort. But I'm unit-testing the isolated method about what it should do. (and I'm personally doing this for every single method, which is very bad and time consuming I think)
Is there any midway when it comes to using stubs and mocks? Because I've taken the second approach and I think I'm abusing it.
IMO the second approach is the best one because you test your model properties and validations one at a time (the "unit" part).
To avoid overhead, you may consider using shoulda. It is really efficient for models unit testing. We're usually using a factory_girl/mocha/shoulda combination for functional testing (factory_girl and mocha are also very helpful to test queries and named scopes). Tests are easy to write, read and maintain :
# class UserTest < ActiveSupport::TestCase
# or
# describe 'User' do
should have_db_column(:weird).of_type(:string).with_options(:limit=>255)
should allow_value("something weird").for(:weird)
should_not allow_value("something").for(:weird)
should ensure_length_of(:weird).is_at_least(1).is_at_most(255)
# end
Shoulda generates positive/negative matchers therefore avoids a lot of code duplication.

Rails assert that form is valid

What's the best practices way to test that a model is valid in rails?
For example, if I have a User model that validates the uniqueness of an email_address property, how do I check that posting the form returned an error (or better yet, specifically returned an error for that field).
I feel like this should be something obvious, but as I'm quickly finding out, I still don't quite have the vocabulary required to effectively google ruby questions.
The easiest way would probably be:
class UserEmailAddressDuplicateTest < ActiveSupport::TestCase
def setup
#email = "test#example.org"
#user1, #user2 = User.create(:email => #email), User.new(:email => #email)
end
def test_user_should_not_be_valid_given_duplicate_email_addresses
assert !#user2.valid?
end
def test_user_should_produce_error_for_duplicate_email_address
# Test for the default error message.
assert_equal "has already been taken", #user2.errors.on(:email)
end
end
Of course it's possible that you don't want to create a separate test case for this behaviour, in which case you could duplicate the logic in the setup method and include it in both tests (or put it in a private method).
Alternatively you could store the first (reference) user in a fixture such as fixtures/users.yml, and simply instantiate a new user with a duplicate address in each test.
Refactor as you see fit!
http://thoughtbot.com/projects/shoulda/
Shoulda includes macros for testing things like validators along with many other things. Worth checking out for TDD.
errors.on is what you want
http://api.rubyonrails.org/classes/ActiveRecord/Errors.html#M002496
#obj.errors.on(:email) will return nil if field is valid, and the error messages either in a String or Array of Strings if there are one or more errors.
Testing the model via unit tests is, of course, step one. However, that doesn't necessarily guarantee that the user will get the feedback they need.
Section 4 of the Rails Guide on Testing has a lot of good information on functional testing (i.e. testing controllers and views). You have a couple of basic options here: check that the flash has a message in it about the error, or use assert_select to find the actual HTML elements that should have been generated in case of an error. The latter is really the only way to test that the user will actually get the message.

Resources