Rspec before(:each) works but before(:all) does not - ruby-on-rails

My ProductCategory spec:-
require 'rails_helper'
RSpec.describe ProductCategory, type: :model do
before(:each) do
#product_category = create(:product_category)
end
context "validations" do
it "should have valid factory" do
expect(#product_category).to be_valid
end
it "should have unique name" do
product_category_new = build(:product_category, name: #product_category.name)
expect(product_category_new.save).to be false
end
end
end
The spec runs fine, but when I use before(:all) instead of before(:each), second example fails -
expected false got true I know the difference between before(:all) and before(:each) but I am not able to find the exact reason why second example fails with before(:all)

before :all only runs once before all the examples, so the #product_category is created once. If you have a something like a DatabaseCleaner truncation running after each test, the record is no longer in the database in the second test, thus passing the validation.
before :each on the other hand will be run before each example, so the record will be there in the second example even if the database was cleaned in the meantime.

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

`allow_any_instance_of` mock not working in scope

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

Rspec fails but behaviour works when I test it via controller

This should be very simple to test, but for some reason my test is failing, please consider the following, model bit:
class User < ActiveRecord::Base
def active!
update_attribute(:active, true)
end
end
controller :
def activate
user = User.find_by_uuid(params[:id])
user.active!
puts "id #{user.id}"
end
test :
describe 'activate user' do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit activate_user_path(id: user.uuid)
puts "id #{user.id}"
end
it 'should be true' do
save_and_open_page
user.active.should be_true
end
end
This test fails :
expected: true value
got: false
But when I do it with browser, the user gets activated without problems. What am I doing wrong? This really looks like a sily test but still doesn't pass, I've spend more than one hour trying out different stuff, none of which worked.
The problem is that the spec still holds the User in the user variable that was created via FactoryGirl and does not know that is was changed in the database. Just reload the user and it should work:
it 'should be true' do
save_and_open_page
user.reload.active.should be_true
end
Btw. if active is a boolean you can also spec it this way (what reads much nicer):
user.reload.should be_active

RSpec - How to run FactoryGirl.create before each test except for one in a describe block

I have RSpec tests and within one describe block I use FactoryGirl to create a model before (:each) test. For one test within my describe block I would like to create a Factory with different attributes. Any ideas how this is best done?
You could create the object in before(:each) and change the attribute for the same object in your test that needs it
describe "test" do
before(:each) do
#model = FactoryGirl.create(:model)
end
it "should do something" do
#model.update_attribute(:attribute, :value)
end
end
Another way:
describe "test" do
before(:each) do
#model = FactoryGirl.create(:model)
end
it "should do something" do
model1 = FactoryGirl.create(:model, :attribute1 => "value")
end
end

Clearing out ActionMailer::Base.deliveries after RSpec test

I have the following RSpec test for my UserMailer class:
require "spec_helper"
describe UserMailer do
it "should send welcome emails" do
ActionMailer::Base.deliveries.should be_empty
user = Factory(:user)
UserMailer.welcome_email(user).deliver
ActionMailer::Base.deliveries.should_not be_empty
end
end
This test passed the first time, but failed the second time I ran it. After doing a little bit of debugging, it appears that the 1st test added an item to the ActionMailer::Base.deliveries array and that item never got cleared out. That causes the first line in the test to fail since the array is not empty.
What's the best way to clear out the ActionMailer::Base.deliveries array after an RSpec test?
RSpec.describe UserMailer do
before do
# ActionMailer::Base.deliveries is a regular array
ActionMailer::Base.deliveries = []
# or use ActionMailer::Base.deliveries.clear
end
it "sends welcome email" do
user = create(:user)
UserMailer.welcome_email(user).deliver_now
expect(ActionMailer::Base.deliveries).to be_present
end
end
You can clear the deliveries after each test quite easily, adding this into your spec_helper.rb.
RSpec.configure do |config|
config.before { ActionMailer::Base.deliveries.clear }
end
I'd suggest reading my article about the correct emails configuration in Rails where I talk also about testing them correctly.
As Andy Lindeman points out, clearing the deliveries is done automatically for mailer tests. However, for other types, simply add , :type => :mailer to the wrapping block to force the same behavior.
describe "tests that send emails", type: :mailer do
# some tests
end

Resources