When I use shared_examples_for as shown below, my test, "makes an auth call" gets skipped. Why is this? If I comment out the shared_examples line then my assertion fails despite it being called in let!. I verified with pry that the function call is taking place. Can someone please explain why I'm seeing these behaviors and how to fix it. Thanks!
# frozen_string_literal: true
require "rails_helper"
describe MyFunc::Auth do
describe ".get_value" do
before do
allow(JWT).to receive(:decode).and_return("myDecodedValue")
end
shared_examples_for "authentication" do
context "success" do
let!(:stub_request) { stub_token(status: 200) }
it "makes an auth call" do
caller
expect(stub_request).to have_been_requested
end
end
end
end
end
You're only defining the shared example, after this you need to actually call it by
include_examples "authentication"
# or
it_behaves_like "authentication"
You can read about the difference in the docs.
Related
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
My mock is only working when it's in the before block shown below. This is just my quick and dirty representation of my problem. Literally when I move the line from the before block to the does not quack assertion, it stops mocking :(
describe 'Ducks', type: :feature do
before do
...
allow_any_instance_of(Duck).to receive(:quack).and_return('bark!')
visit animal_farm_path
end
context 'is an odd duck'
it 'does not quack' do
expect(Duck.new.quack).to eq('bark!')
end
end
end
I want it here, but it doesn't work:
describe 'Ducks', type: :feature do
before do
...
visit animal_farm_path
end
context 'is an odd duck'
it 'does not quack' do
allow_any_instance_of(Duck).to receive(:quack).and_return('bark!')
expect(Duck.new.quack).to eq('bark!')
end
end
end
My bad. The original question was poorly written. Visiting the page is what makes the #quack call. The mocks must always be done before you do whatever it is that engages the method call. So this was my solution
describe 'Ducks', type: :feature do
before do
...
end
context 'is an odd duck'
it 'does not quack' do
allow_any_instance_of(Duck).to receive(:quack).and_return('bark!')
visit animal_farm_path
# In this crude example, the page prints out the animals sound
expect(page).to have_text('bark!')
end
end
end
I have a controller spec like this :
describe "#create" do
before { post 'create', params }
context "when the artist is valid" do
before { allow(artist).to receive(:save).and_return(true) }
it { expect(page).to redirect_to(root_path) }
it { expect(notifier).to have_received(:notify) }
end
end
This is a simple spec but It doesn't work because the describe's before block is executed before the context's before block. So, the result of artist.save is not stubed when the create action is called.
It tried to do this :
describe "first describe" do
before { puts 2 }
describe "second describe" do
before { puts 1 }
it "simple spec" do
expect(1).to eq 1
end
end
end
I see the "2" before the "1". I'm not sure but I think it was working with previous versions.
I know, I can do this :
describe "#create" do
context "when the artist is valid" do
before { allow(artist).to receive(:save).and_return(true) }
it "redirect to the root path" do
post 'create', params
expect(page).to redirect_to(root_path)
end
it "do notifications" do
post :create, params
expect(notifier).to have_received(:notify)
end
end
end
But I think it's less clean.
I found, on this page, http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/Hooks#before-instance_method than the order should be this :
before(:suite) # declared in RSpec.configure
before(:all) # declared in RSpec.configure
before(:all) # declared in a parent group
before(:all) # declared in the current group
before(:each) # declared in RSpec.configure
before(:each) # declared in a parent group
before(:each) # declared in the current group
It's not the case on this example.
I'm not sure but I think it was working with older versions of rspec.
Is there a solution?
I would strongly recommend against you changing the order of hooks in rspec. That will make your app non-standard and Rails is build on standards and having things work as expected.
Everything you're describing it "as designed". Outer before blocks are always called before inner blocks.
Your example that you feel is "less clean" is the standard way to do controller specs. I actually encourage you to do it this way so that it is more maintainable/readable. It does not look unclean to me at all.
That said, there are some options:
You can use a method. I have more than once had a method that was do_post or something similar
You can use a let block which is initialized lazily. I would find it unlcean if it relied on other before blocks running first, but it's an option.
You can define subject. https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/subject/explicit-subject
here is my rspec code:
describe User do
before{(#user=User.new(username:"abcdefg",email:"123456#123.com",password:"123456")}
subject(#user)
#user.save
end
and I got such an error : undefined method 'save' for nil:NilClass(NoMethodError)
I try to write the same code in the rails console,it just worked. But when it comes to Rspec,it failed and I'm not able to find any reason...
Could any one help me with it?
here is the Rspec way:
describe User do
let(:valid_user) { User.new(username:"abcdefg",email:"123456#123.com",password:"123456") }
it "can be saved" do
expect(valid_user.save).to be_true
end
end
Note that you should avoid database operations in your specs, it's what make them slow.
Another point, consider using factories to clean up your specs.
You need to wrap the code in an example block (i.e., call the it method with a block), because in the context of the describe block, #user is not defined. For example:
describe User do
before{(#user=User.new(username:"abcdefg",email:"123456#123.com",password:"123456")}
subject(#user)
it "can be saved" do
#user.should respond_to(:save)
#user.save.should_not be_false
end
end
Edit: I noticed also that you have subject(#user) but that may need to be a block in order to set it properly. The following is cleaner overall:
describe User do
let(:user) { User.new(username:"abcdefg",email:"123456#123.com",password:"123456") }
it "can be saved" do
user.should respond_to(:save)
user.save.should_not be_false
end
end
I have an Ruby on Rails 3 admin_controller with the default set of CRUD, index and so on methods. I'd like to test each of these for certain assertions with rspec.
Like response.should render_template("layouts/some_layout") or tests that it should require login.
Copy-pasting that test into the group of tests for each method is a lot of duplication. IMO it makes little sense to have an
it 'should require login' do
Duplicated several times troughout that test.
Is there a simple way to run a test on a list of methods? Say defined_methods.each do |method| it 'should' .... of some sort?
Is this a good way in the first place? Or am I taking a wrong route in the first place?
Given that you really want all those assertions, have you considered shared example groups?
shared_examples_for "an action that requires authentication" do
it "should render successfuly" do
sign_in(user)
response.should be_success # or whatever
end
it "should deny access" do
# don't sign_in the user
# assert access was denied
end
end
shared_examples_for "another behaviour" do
# ...
end
let(:user) { create_user }
describe "#index" do
before(:each) { get :index }
it_behaves_like "an action that requires authentication"
it_behaves_like "another behaviour"
end
describe "#show" do
before(:each) { get :show }
it_behaves_like "an action that requires authentication"
end
# ...
Of course before writing large number of specs for a basic functionality you should always check if it isn't already tested by the library that is providing the functionality (e.g. checking for the rendered template, if it is handled by rails's implicit rendering, might be a bit overkill).
If you wanted to go down the route of iteratively testing each public method in the controller, you could do something like:
SomeController.public_instance_methods(false).each do |method|
it "should do something"
end
However, I think a shared example group (see about half way down this page: http://rspec.info/documentation/) would be prettier. If it were extracted so it could be used across all your controller specs, it'll be even nicer..
shared_examples_for "admin actions" do
it "should require login"
end
Then in each controller spec:
describe SomeController do
it_should_behave_like "admin actions"
end
Just add it to your test_helper.rb, something like:
def requires_login
...
end