32

It's my understanding that Rails' testing environment is torn down and rebuilt before each test...so how do I test a controller that requires that a user be logged in and that user can't be created without Device's confirmable module getting in the way?

Devise's recommended method (below) creates a new user which is then sent an email by Devise's confirmable module. How do I get around this so I'm not 'creating' a user each time...or if I am I can get an object to test w/out "simulating" a new email for each spec?

 before(:each) do
    @user = Factory.create(:user)
    sign_in @user
  end

I'm sure I'm overlooking something painfully obvious as this must be a very common spec for anyone using Devise with confirmable...

Meltemi
  • 37,979
  • 50
  • 195
  • 293

8 Answers8

35

In your test environment ActionMailer::Base.delivery_method should be set to :test, which means that these emails will not be sent out. If this setting is set to something else such as smtp by way of a configuration in say config/environments.rb, then emails will be sent out.

If that setting's already there, then to use the User object (as in, to be actually able to log in) you'll need to call confirm! on it:

user = User.first
user.confirm!
Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
  • you could also move the creation of the user to a `before(:all)` block, to speed it up... – iain Dec 13 '10 at 21:38
  • I did have to add `config.action_mailer.default_url_options = { :host => 'localhost:3000' }` to get tests to run...otherwise `test.rb` is boilerplate, to my knowledge. Only mail related configs in `test.rb` or `environment.rb` is: ` config.action_mailer.delivery_method = :test`. – Meltemi Dec 13 '10 at 21:41
  • 1
    so is `ActionMailer::Base.delivery_method = :test` different in some way to the auto-generated `config.action_mailer.delivery_method = :test`?? – Meltemi Dec 13 '10 at 21:43
  • 1
    ok. backing up. the emails weren't sent out to begin with because of the test.rb setting. The problem was that the user record wasn't active (or whatever term Devise uses) so they couldn't log in... Every time the user is created before these tests it sits in a limbo state waiting to be "confirmed" while the tests fail... – Meltemi Dec 13 '10 at 21:50
  • 4
    @Meltemi: Right, which is why you should be calling `@user.confirm!` on the record before trying to do anything to it. I'll update my answer with that. – Ryan Bigg Dec 13 '10 at 22:28
  • OK, getting closer. `@user.confirm!` is helpful and a step in right direction! but, it's still not working as I've got a new cryptic error: `undefined method 'env' for nil:NilClass` – Meltemi Dec 13 '10 at 23:29
  • this may need to be updated, on devise-4.7.1 it seems `@user.confirm` works but `@user.confirm!` is not defined `NoMethodError: undefined method `confirm!' for # Did you mean? confirm` – lacostenycoder Jun 01 '21 at 17:16
19

for latest FactoryGirl version:

FactoryGirl.define do

  factory :confirmed_user, :parent => :user do
    after(:create) { |user| user.confirm }
  end

end
Kaka Ruto
  • 4,581
  • 1
  • 31
  • 39
enricostn
  • 411
  • 4
  • 13
13

If you are using factory_girl to generate your models, you can use after_create to confirm each new user.

Factory.define :confirmed_user, :parent => :user do |f|
  f.after_create { |user| user.confirm! }
end
Ryan Ahearn
  • 7,886
  • 7
  • 51
  • 56
  • 4
    I believe that you will get the same effect by adding `confirmed_at Time.now` as a factory attribute. – Giuseppe Aug 30 '13 at 16:06
10

For Devise 4.2 it's now confirm (not confirm!).

Source

4.2.0 - 2016-07-01

Remove the Devise::Models::Confirmable#confirm! method, use confirm instead.

Code below also prevents mailers from being generated thanks to after(:build) vs after(:create) for setting confirmed_at

FactoryGirl.define do
  factory :user do
    after(:build)   { |u| u.skip_confirmation_notification! }
    after(:create)  { |u| u.confirm }
    ...
  end
end
Jon Doe
  • 2,172
  • 1
  • 18
  • 35
Brian Sigafoos
  • 503
  • 6
  • 14
8

Today it is just put:

confirmed_at 7.days.ago

on your fabrication

felipero
  • 463
  • 4
  • 13
2

Try stubbing active?

@user = Factory.create(:user)
@user.stub(:active?).and_return(true)
sign_in @user

You might also need to stub confirmed?, I'm not too familiar with Devise.

zetetic
  • 47,184
  • 10
  • 111
  • 119
2

This thread hasn't seen much traffic in a while, but for people who have this question now, all you need to do is create a trait called :confirmed that sets the required attributes, and when you use it to create a user, you will be able to log in with it without requiring multiple saves of the user db records.

  # Use faker library (installed separately) to set realistic seed data
  require 'faker'

  # Factory definition
  factory :user, do
    email { Faker::Internet.unique.email }
    password { Faker::Internet.password(min_length: 12, max_length: 128) }

    trait :confirmed do
      confirmed_at { Time.zone.now }
      confirmation_sent_at { Time.zone.now }
      confirmation_token { '12345' }
    end
  end

  # Example of using it
  user = create(:user, :confirmed)
RSmithlal
  • 386
  • 4
  • 17
1

For fabrication gem it is

Fabricator(:user) do
  after_build { |user| user.confirm! }
end
bonyiii
  • 2,833
  • 29
  • 25