RSpec integration testing - ruby-on-rails

I have trouble with testing model count through feature/scenario rspec instrument
require 'spec_helper'
feature 'Registration' do
scenario 'Guest can sign up as individual' do
with_role(:guest)
sign_up(:client)
Individual.count.should == 1
# should be replaced by expect{ sign_up(:client) }.to change{ Individual.count }.by(1)
end
end
How can I replace model.count should to the expect in the block to expecting in scenario? I don't want to use should because of record count changes every time I use it
Regards,
Alex

The correct way of using the change matcher of rspec is as follows:
expect{ sign_up(:client) }.to change(Individual, :count).by(1)

Related

Before block in Rspec test run after the context

I'm migrating an app from rails 4 to rails 6 and the test are now broken.
I'm not so good in testing, i must improve, but I cannot understand why a before block run after the test.
require 'rails_helper'
RSpec.describe Admin::TrainingEmailsController, type: :controller do
describe "GET #create", :uses_mail do
context "with valid attributes" do
before(:each) do
binding.pry
end
describe "sends the value letter request email" do
binding.pry
it { expect(1+1).to eq(2) }
end
end
end
end
I add a couple of pry, and the when I run rspec "sends the value letter request email" run before the before block and so my ActionMailer::Base.deliveries.last is empty
I try with before, before(:all) and before(:each)
It doesn't.
describe block is not running a test yet. It's just defining it.
only it or specify blocks, when run are executing a test. Put binging.pry inside the it block to see it:
describe "sends the value letter request email" do
binding.pry
let(:mail) { ActionMailer::Base.deliveries.last }
let(:request_sender) { "ValueLetters#vl.balancedview.org" }
let(:accepted_participants_emails) do
training.participants.accepted.
map(&:email)
end
it do
binding.pry # <-- here
expect(mail.to).to match(accepted_participants_emails) }
end
end

Avoid excessive rspec nesting with subject, let, and alternative arguments

I'm trying to do some model_spec testing but having trouble with not having to further nest my rspec code. It would be great if in this case, I could just have a set of "it's" instead of having to add context everytime I want to switch the variable var. Here's the following code:
describe "#some_method" do
subject { course.some_method(var) }
context 'given a project' do
let(:var) {random[1]}
it 'returns the one after' do
is_expected.to eq(random[2])
end
context 'being the last' do
let(:vars) {random.last}
it 'returns nil' do
is_expected.to be_nil
end
end
context '...you get the point, being something else' do
let(:vars) { something.else }
it 'returns nil' do
is_expected.to.to be_nil
end
end
end
end
Maybe I'm just stuck in the wrong mode of thinking and someone could think of a better way for me to do this? I've been suggested that I absolutely must use the subject by someone I work for.
At first, I disagreed and thought it was getting a little burdensome but then I figured keeping subject and having let(:var) apply to it was pretty useful...
RSpecs subject is a tool which can be used to make tests more succinct. There are many cases where it makes sense to use the subject:
RSpec.describe User do
# with the help of shoulda-matchers
it { should validate_uniqueness_of :username } # implicit subject
end
RSpec.describe UsersController do
describe '#show' do
it 'is successful' do
get :show
expect(response).to have_http_status :success
end
it 'renders template show' do
get :show
expect(response).to render_template :show
end
end
#vs
describe '#show' do
subject { response }
before { get :show }
it { should have_http_status :success }
it { should render_template :success }
end
end
And there are cases where using subject will hurt the readability and acuity of your tests.
Your college is just plain wrong in insisting that you always use subject.
A good rule of hand is that if you need an it block then you should not be using subject or is_expected.
If you are describing the call signature of a method you should be calling it in your specs in the same way you would in real life.
let(:decorator){ described_class.new(user) }
describe "#link" do
it 'takes a class option' do
expect(decorator.link(class: 'button')).to match /class=\"button/
end
end
I would recommend running rspec with the --format documentation option and checking if the output actually makes sense. This can be quite important once you get 100s of specs as it gets harder to remember what a behavior a spec actually covers.
How about you write it like this?
expect(subject.call(foo)) is not very pretty but it gets rid of the nesting.
describe "#some_method" do
subject { course.method(:some_method) }
it 'returns the one after if given a project' do
expect(subject.call(random[1])).to eq(random[2])
end
it 'returns nil when it is the last' do
expect(subject.call(random.last)).to be_nil
end
it 'returns nil...' do
expect(subject.call(something.else)).to be_nil
end
end

Writing multiple feature spec expectations from a single action

So I'm writing my feature test and it goes something like this, note the [[place_holder]]
feature 'Accounts' do
scenario 'creating an account' do
# visit and fill form
expect {
click_button 'Create Account'
}.to [[place_holder]]
success_message = 'Your account has been successfully created.'
expect(page).to have_content(success_message)
end
end
Now I want to somehow place 2 expecations for this block, these expectations are
change(User, :count).by(1)
change(Account, :count).by(1)
Is there a way I could like chain these two exectations into one, yea I know I could just do a test for each in it's scenario, but that code is too WET, no need for duplications, and the feature specs are slow to begin with, no need to make my test suite slower.
Any suggestions/alternatives are appriceated
From rspec 3.1, you can use compound matcher expressions with block expectations.
expect {
click_button 'Create Account'
}.to change(User, :count).by(1).and change(Account, :count).by(1)
http://rspec.info/blog/2014/09/rspec-3-1-has-been-released/#expectations-block-matchers-can-now-be-used-in-compound-expressions
Since the data will presumably be cleared between each test run, you could just check the absolute value, e.g.
expect(User.count).to eq(1)
expect(Account.count).to eq(1)
I think this is more readable.

RSpec/Mongoid: Expect to change count on embedded models

I have two Mongoid models: User and EmailAccounts. The latter is embedded in the User model. That configuration should be fine because it works generally.
Now I'm trying to write an integration test for my user edit form that looks like this:
describe 'Add EmailAccount' do
it 'Adds an email account', js: true do
user = FactoryGirl.create(:user_without_email_accounts)
visit edit_user_path(user)
expect{
click_link 'New Email Account'
within '.nested-fields' do
fill_in 'Account Name', with: 'New Email Account'
fill_in 'Other Field', with: 'Other Data'
end
click_button 'Save'
}.to change(EmailAccount, :count).by(1)
end
end
Because EmailAccount is an embedded model the change of count is always 0.
Can I check for a change of the EmailAccount counter in any similar way? Or do I have to go a different way?
This won't work neither:
}.to change(user.email_accounts, :count).by(1)
I had the exact same issue and managed to solve it by using a combination the answers posted here.
expect {
#action
}.to change { foo.reload.bars.count }.by(1)
Edited with new answer:
I've been able to use this syntax in my spec of a Mongoid document:
expect {
#action
}.to change { Model.count }.by(1)
Note that the count statement is within brackets and doesn't use a :count parameter.
try following:
user.reload
}.to change(user.email_accounts, :count).by(1)

How does RSpec's expect work in ROR

While going through Ruby On Rails Tutorial by Michael Hartl,in the section where the author writes integration test to validate his Signup page, he has used code spinet bellow. I got what the code does but couldn't get my head around the 'how' part i.e. couldn't understand order of execution.
expect { click_button "Create my account" }.not_to change(User, :count)
Can someone please explain the semantics of the above chain of methods and blocks and how they fit together?
You'd use expect ... change to verify that a particular method call changes -- or does not change -- some other value. In this case:
expect { click_button "Create my account" }.not_to change(User, :count)
will cause rspec to do the following:
Run User.count and note the value returned. (This can be specified as a receiver and a method name, like (User, :count) in your example, or as an arbitrary block of code, like { User.count }.
Run click_button "Create my account" which is a Capybara method that simulates a mouse click on a link.
Run User.count again.
Compare the results of #1 and #3. If they differ, the example fails. If they're the same, it passes.
Other ways to use expect ... change:
expect { thing.destroy }.to change(Thing, :count).from(1).to(0)
expect { thing.tax = 5 }.to change { thing.total_price }.by(5)
expect { thing.save! }.to raise_error
expect { thing.symbolize_name }.to change { thing.name }.from(String).to(Symbol)
Some docs are here.
How it does this is a bit arcane, and it's not at all necessary to understand how it works in order to use it. A call to expect is defining a structure for rspec to execute, using rspec's own custom DSL and system of "matchers." Gary Bernhardt has a rather neat screencast in which he argues that the mystery of rspec actually falls out naturally from a dynamic language like ruby. It's not a good introduction to using rspec, but if you're curious about how it all works, you might find it interesting.
UPDATE
After seeing your comment on another answer, I'll add a bit about the order of operations. The unintuitive trick is that it's the matcher (change in this case) that executes all of the blocks. expect has a lambda, not_to is an alias for should_not whose job is to pass the lambda on to the matcher. The matcher in this case is change which knows to execute its own argument once, then execute the lambda that it was passed (the one from expect), then run its own argument again to see if things changed. It's tricky because the line looks like it should execute left to right, but since most of the pieces are just passing around blocks of code, they can and do shuffle them into whatever order makes the most sense to the matcher.
I'm not an expert on the rspec internals, but that's my understanding of the basic idea.
Here's an excerpt from from Ryan Bates Railscast on Request Specs and Capybara
require 'spec_helper'
describe "Tasks" do
describe "GET /tasks" do
it "displays tasks" do
Task.create!(:name => "paint fence")
visit tasks_path
page.should have_content("paint fence")
end
end
describe "POST /tasks" do
it "creates a task" do
visit tasks_path
fill_in "Name", :with => "mow lawn"
click_button "Add"
page.should have_content("Successfully added task.")
page.should have_content("mow lawn")
end
end
end
And here's an excerpt from the docs on RSPec Expectations
describe Counter, "#increment" do
it "should increment the count" do
expect{Counter.increment}.to change{Counter.count}.from(0).to(1)
end
# deliberate failure
it "should increment the count by 2" do
expect{Counter.increment}.to change{Counter.count}.by(2)
end
end
So basically, the
expect { click_button "Create my account" }.not_to change(User, :count)
is part RSpec:
expect {...}.not_to change(User, :count)
and part Capybara
click_button "Create my account"
(Here's a link to the Capyabara DSL -- you can search for click_button)
It sounds like you're looking for an overall example with them both. This isn't a perfect example, but it could look something like this:
describe "Tasks" do
describe "GET /tasks" do
it "displays tasks" do
expect { click_button "Create my account" }.not_to change(User, :count)
end
end
end

Resources