I have this code to test ActiveJob and ActionMailer with Rspec
I don't know how really execute all enqueued job
describe 'whatever' do
include ActiveJob::TestHelper
after do
clear_enqueued_jobs
end
it 'should email' do
expect(enqueued_jobs.size).to eq(1)
end
end
The proper way to test will be to check number of enqueued jobs as in your example, and then test each job separately. If you want to do integration testing you can try perform_enqueued_jobs helper:
describe 'whatever' do
include ActiveJob::TestHelper
after do
clear_enqueued_jobs
end
it 'should email' do
perform_enqueued_jobs do
SomeClass.some_action
end
end
end
See ActiveJob::TestHelper docs
Here is how I solved a similar problem:
# rails_helper.rb
RSpec.configure do |config|
config.before :example, perform_enqueued: true do
#old_perform_enqueued_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_jobs
#old_perform_enqueued_at_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
end
config.after :example, perform_enqueued: true do
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = #old_perform_enqueued_jobs
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = #old_perform_enqueued_at_jobs
end
end
Then in specs we can use:
it "should perform immediately", perform_enqueued: true do
SomeJob.perform_later
end
Just combined all the best pieces, +included sidekiq:
spec/support/perform_jobs.rb:
require 'sidekiq/testing'
RSpec.configure do |config|
Sidekiq::Testing.fake!
config.around(:each, perform_jobs: true) do |example|
Sidekiq::Testing.inline! do
queue_adapter = ActiveJob::Base.queue_adapter
old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
queue_adapter.perform_enqueued_jobs = true
queue_adapter.perform_enqueued_at_jobs = true
example.run
ensure
queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
end
end
end
spec/some_spec.rb:
it 'works', perform_jobs: true do
...
end
I have an :inline_jobs helper, where applied to a test it will perform the enqueued jobs and clear the enqueued jobs
module InlineJobHelpers
def self.included(example_group)
example_group.around(:each, :inline_jobs) do |example|
perform_enqueued_jobs do
example.run
end
ensure
clear_enqueued_jobs
end
end
end
RSpec.configure do |config|
config.include ActiveJob::TestHelper, :inline_jobs
config.include InlineJobHelpers, :inline_jobs
end
Related
In our project we have rake task, that creates mailer templates in database.
While running all specs, some of them are failing saying that the db record for some mailers don't exist.
Any ideas on what could be the reason for this and how to fix it?
Here the example of the task to create Mailer template.
desc "Creates mailer templates"
task create_mailers: :environment do
MailerTemplate.delete_all
I18n.available_locales.each do |locale|
MailerTemplate.create(locale: locale, mailer: "info#action", content: %{<p>Content</p>})
end
end
This is my database_cleaner.rb file
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:deletion)
Rake::Task["create_mailers"].invoke
end
config.around(:each) do |example|
DatabaseCleaner.strategy = example.metadata[:js] ? :deletion: :transaction
DatabaseCleaner.cleaning do
example.run
end
end
config.after(:each) do
Capybara.reset_sessions!
DatabaseCleaner.clean
end
end
I have a simple scenario in my rspec feature:
require 'spec_helper'
feature 'CLIENT views results page' do
context 'from welcome/index form' do
let!(:location) { FactoryGirl.create :location, name: 'town' }
before :each do
visit '/'
end
scenario 'successfully', js: true do
expect(Location.count).to eq 1
fill_in 'from_address', with: 'Some address'
fill_in 'to_address', with: 'Another address'
click_button 'Search'
expect(page).to have_content 'Some address → Another address'
end
end
end
spec_helper:
require 'rubygems'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'factory_girl'
require 'database_cleaner'
require 'capybara/poltergeist'
FactoryGirl.reload
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.infer_base_class_for_anonymous_controllers = true
config.order = "random"
config.tty = true
config.mock_with :rspec
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.treat_symbols_as_metadata_keys_with_true_values = true
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
Capybara.javascript_driver = :poltergeist
Capybara.asset_host = "http://localhost:3000"
Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i
And my scenario fails on some random step (after expect(Location.count).to eq 1) due to the fact, that there are no Location records:
(byebug) Location.all
[]
I think it might be one of two cases:
Either DatabaseCleaner cleans database before an example finishes
Or there are actually two threads with two databases, one of which (the one that is used by the app) is really empty
Gem versions:
rspec (2.14.1)
rspec-core (2.14.8)
rspec-rails (2.14.0)
phantomjs (1.9.7.1)
rails (3.2.21)
database_cleaner (0.9.1)
I remember having issues with this as well. My config for DB cleaning looks like this:
config.before(:each) do
if Capybara.current_driver == :rack_test
DatabaseCleaner.strategy = :transaction
else
DatabaseCleaner.strategy = :truncation
end
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
Maybe it has something to do with the order your before blocks are called. So if DatabaseCleaner.start is called before you wanted strategy is set, you might see such results.
Managed to solve this issue by migrating to
config.use_transactional_fixtures = true
instead of using DatabaseCleaner. Some workarounds are suggested to make transactional cleaning work with javascript tests. I chose this one:
(adding somewhere in the beginning of spec/spec_helper)
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
cause it seemed to work faster, still leaving some spontaneously failing tests (might not be the problem related to database).
So in my app I can disable the cache for all tests, which would be ideal, but apparently there are a number of legacy tests that rely on the cache being functional. Is there a way to enable the Rails cache for a single RSpec test?
Something like:
before(:each) do
#cache_setting = Rails.cache.null_cache
Rails.cache.null_cache = true
end
after(:each) do
Rails.cache.null_cache = #cache_setting
end
it 'does not hit the cache' do
...
end
in spec_helper.rb
RSpec.configure do |config|
config.before(:example, disable_cache: true) do
allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::NullStore.new)
end
config.after(:example, disable_cache: true) do
allow(Rails).to receive(:cache).and_call_original
end
end
in xxx_spec.rb
RSpec.describe "a group without matching metadata" do
it "does not run the hook" do
puts Rails.cache.class
end
it "runs the hook for a single example with matching metadata", disable_cache: true do
puts Rails.cache.class
end
end
https://www.relishapp.com/rspec/rspec-core/docs/hooks/filters
How can I tag an example group so that the database isn't cleaned between each example, but is cleaned before and after the whole group? And untagged specs should clean the database between each example.
I would like to write:
describe 'my_dirty_group', :dont_clean do
...
end
So in my spec_helper.rb I put:
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:suite, dont_clean: true) do
DatabaseCleaner.clean
end
config.after(:suite, dont_clean: true) do
DatabaseCleaner.clean
end
config.before(:each, dont_clean: nil) do
DatabaseCleaner.start
end
config.before(:each, dont_clean: nil) do
DatabaseCleaner.clean
end
The problem is that the dont_clean: nil (or false) blocks in spec_helper don't run when the metadata tag is not specified. Is there another way to check for presence of :dont_clean before cleaning between examples?
Summary
You can set custom metadata on the whole example block, and then access the metadata within your RSpec config with self.class.metadata for use with conditional logic.
Code
Using these gem versions:
$ bundle exec gem list | grep -E '^rails |^rspec-core |^database'
database_cleaner (1.4.0)
rails (4.2.0)
rspec-core (3.2.0)
The following works for me:
File: spec/spec_helper.rb
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.before(:all) do
# Clean before each example group if clean_as_group is set
if self.class.metadata[:clean_as_group]
DatabaseCleaner.clean
end
end
config.after(:all) do
# Clean after each example group if clean_as_group is set
if self.class.metadata[:clean_as_group]
DatabaseCleaner.clean
end
end
config.before(:each) do
# Clean before each example unless clean_as_group is set
unless self.class.metadata[:clean_as_group]
DatabaseCleaner.start
end
end
config.after(:each) do
# Clean before each example unless clean_as_group is set
unless self.class.metadata[:clean_as_group]
DatabaseCleaner.clean
end
end
end
File: spec/models/foo_spec.rb
require 'spec_helper'
describe 'a particular resource saved to the database', clean_as_group: true do
it 'should initially be empty' do
expect(Foo.count).to eq(0)
foo = Foo.create()
end
it 'should NOT get cleaned between examples within a group' do
expect(Foo.count).to eq(1)
end
end
describe 'that same resource again' do
it 'should get cleaned between example groups' do
expect(Foo.count).to eq(0)
foo = Foo.create()
end
it 'should get cleaned between examples within a group in the absence of metadata' do
expect(Foo.count).to eq(0)
end
end
You can check on example.metadata inside the blocks, although I haven't been able to figure out how to do this for before(:suite)
I am using Ruby on Rails 3.2.2 and rspec-rails-2.8.1. In order to make my spec files DRY (Don't Repeat Yourself) and to seed the test database I would like to run a before(:each) hook for all those spec files. That is, in all my spec files I have the following code:
describe 'test description' do
before(:each) do
load "#{Rails.root}/db/seeds.rb"
end
...
end
Is it possible to add "somewhere" that before(:each) hook so that all spec files can run it? What do you advice?
In the spec_helper.rb:
RSpec.configure do |config|
#your other config
config.before(:each) do
#your code here
end
end
There is much configuration available. For instance: config.before(:each, :type => [:routing, :controller, :request])
You can even create your own tags and associate code to it:
config.around :each, unobstrusive: true do |example|
Capybara.current_driver = :rack_test
example.run
Capybara.current_driver = :selenium
end
You can add before/after hooks in your Rspec.configure block, usually in your spec_helper:
RSpec.configure do |config|
config.before(:each) do
...
end
end