I'm in the middle of trying to upgrade pieces of our app to Rails 3, specifically our unit tests. I'm consistently running into an issue with delayed job and mailers, in particular, namely that the mailers aren't getting "sent" when the test is run.
To test emails sent, we run them all through an "assert_email" method in our test helper, which looks more-or-less like this:
def process_delayed_jobs
while Delayed::Worker.new(:quiet => true).send(:reserve_and_run_one_job) do
# nothing
end
end
alias_method :deliver_delayed_emails, :process_delayed_jobs
def assert_emails(number, skip_assertion = false)
deliver_delayed_emails # start with a clean slate
if block_given?
original_count = ActionMailer::Base.deliveries.size
yield
deliver_delayed_emails
new_count = ActionMailer::Base.deliveries.size
assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent" unless skip_assertion
else
assert_equal number, ActionMailer::Base.deliveries.size unless skip_assertion
end
end
We test on this like so:
assert_emails 2 do
Model.action_that_sends_mailer
end
This code works flawlessly in Rails 2, running delayed_job 2.0.5. On Rails 3 we are running 2.1.4.
Basically, this fails every time we test it, because "0" emails get sent. The mailers themselves are upgraded properly, and I've removed the erroneous ".deliver" from the method calls to fire them off. The delayed_jobs log is empty, so it doesn't look like anything is being processed.
I feel like I'm missing some kind of key (but easy) syntax problem here. Any help is appreciated.
Edit: I should also note that I've started the delayed_job process on my local machine with both "rails delayed_job start" and "script/delayed_job start." Neither fix the problem.
Turned out to be something unrelated to delayed_job.
Related
While upgrading a middle size app from Rails 5.1 to 6.0.2.2 (+ Ruby 2.6.1 -> 2.6.3) I start to get flaky tests in all kind of tests. 90% coverage, 500 hundreds tests, and between 0 to 8 tests failings, totally random. After a long bug hunt, I noticed that I got everything working with 100% confidence if I skip all Websocket related tests.
This is typically what I have to test and how I'm doing it (Minitest/Spec syntaxe):
class Api::PlayersControllerTest < ActionController::TestCase
before do
#user = users(:admin)
sign_in #user
end
it "broadcasts stop to user's player" do
put :update, format: :json, params: {id: #user.id}
assert_broadcast_on("PlayersChannel_#{#user.id}", action: "stop")
end
end
Notice that it's not an "integration test" because we're using a raw API call. What I have to check is: if some request is coming to some controller, ActionCable is broadcasting a Websocket message. That's why I have to use Devise sign_in helper in a controller Test.
ActionCable is backed by Redis, in all environments.
I do not use Parallel testing.
Dataset is using Fixtures, not factories.
use_transactional_tests is set to true
I have 23 tests like this one, and they all used to pass without any problem using Rails 5.1. Started one by one using a focus, they also all pass 100% both in Rails 5 or 6. The problem is when executing the whole test suite, I start to get flakiness in all sections (Unit/Models tests included), mostly related to dataset consistency. Actually, it looks like fixtures are not (or poorly) reloaded.
Any ideas? Is it something wrong with what I'm doing, or do you think it's a Rails 6 issue?
Ok, solved by adding Database Cleaner Gem. Pretty curious that it works using the same ":transaction" strategy that's in use with the Rails barebone fixture management... but anyway, it works!
After upgrading to Rails 6 I am noticing that default mailer's .deliver_later is not working the same as in Rails 5.
Configuration:
config.active_job.queue_adapter = :inline
When running Mailer.register_email(...).deliver_later - nothing is stored in ActionMailer::Base.deliveries. This array gets filled if I run perform_enqueued_jobs - it seams like queue_adapter = :inline doesn't work the way I expect it to work.
If I run Mailer.send(...).deliver_now then ActionMailer::Base.deliveries has proper value in it.
Any idea why this is happening and how to solve this?
I had same problem in my tests. A search on the Internet yielded nothing, so I started experimenting.
I tried wrapping the call method of sending mail in
assert_emails 1 do
Mailer.register_email(...).deliver_later
end
After that, ActionMailer::Base.deliveries populated correctly.
If the exact number of emails could easily change, this is another option:
assert_changes 'enqueued_jobs.size' do
# Some code that sends email with deliver_later
end
This allows you to test that emails were sent but it disregards the exact number (which is a limitation of the asserts_emails method - other than this, the asserts_emails method is great).
I found that the enqueued_jobs method is very helpful in testing anything background jobs, including deliver_later
NOTE: the above example only checks that the enqueued jobs list was changed. If you want to be more specific and check that the queue was changed with emails, you should do this:
assert_changes 'enqueued_jobs.select {|job| job["job_class"] == "ActionMailer::MailDeliveryJob"}.size' do
# Some code that sends email with deliver_later
end
The issue
The issue lies in two new lines of code added to Rails 6 (line 1 & line 2),
where basically, the callback before_setup defined here (in RSpec) and here (in Minitest) gets overridden (by this), thus forcing the queue_adapter to be the test adapter instead of the one defined by config.active_job.queue_adapter.
Workaround
So in order to use the queue_adapter defined by config.active_job.queue_adapter and therefore restore the Rails 5 behaviour we can do something like the below.
# spec/support/active_job/test_helper.rb
module ActiveJob
module TestHelper
def before_setup
super
end
end
end
Currently, I'm triggering a SuckerPunch Job via clockwork in my Rails app.
User.find_each do |user|
puts "Found user: #{user.name}!"
end
# Doesn't work, too.
User.all.each do |user|
puts "Found user: #{user.name}!"
end
However, the execution stucks when searching for the users. When printing the Enumerator (User.find_each.inspect) to the console it shows the users. Why won't the block run? May there be a Rails context missing?
It's been driving me crazy for hours. I'm deploying to Heroku, btw.
EDIT: I figured out, there's an error: Primary key not included in the custom select clause.
Thanks in advance!
I suspect you're hitting some sort of limit due to the number of Users in the db. Have you tried using find_in_batch instead?
http://apidock.com/rails/ActiveRecord/Batches/find_each (uses find_in_batch with a default batch_size of 1000)
http://apidock.com/rails/ActiveRecord/Batches/find_in_batches (note the last post)
I am using Redis + Resque in production and want to test that jobs are getting queued and run properly. I am looking for something like this
Resque.jobs(:queue_name).size.should == 0
post :some_action # This action causes a Resque job to be enqueued
# Test Enqueuing
Resque.jobs(:queue_name).size.should == 1
Resque.jobs(:queue_name).last.klass.should == "MyJob"
Resque.jobs(:queue_name).last.args.should == [1, "Arg_2"]
# Test performing
Resque.jobs(:queue_name).perform_all
# test the effect of running the job
How do I start Redis + Resque in test environment? I don't people to manually run a redis server all the time. I have tried the solution where you try and run the redis server in config.before(:suite) but the redis-server never starts up in time and the Resque complains that it can't connect to Redis.
I have tried using Resque.inline but 1) It doesnt let me test that the Job was enqueued 2) It always enqueues the job inside the :inline queue (I want to test that the job ends up in the correct queue).
Personally, I rely on gems which I include in my project, including Resque and Redis, to be tested by the developers who write them. As a result, I do not include testing them in my test suite. For example, when choosing a gem for my application, I look at the gem's documentation to see if TravisCI / Code Climate / etc. statistics are included and if the project is "green." If it is, I use it. If it's not, I look for an earlier (e.g. more stable) version, or look for alternatives. In the case of Resque and Redis for Rails, both of these are well maintained and popular, thus extremely stable.
For my apps, I simply write tests where I present expectations of messages being called to Resque / Redis. For example:
it "should make a call to Resque for #my_job" do
expect(Resque).to_receive(:enqueue).with(SomeJob, args)
my_method_which_calls_resque
end
Then, assuming that you have a method which you are testing called my_method_which_calls_resque that looks something like:
def my_method_which_calls_resque
...
Resque.enqueue(SomeJob, args)
...
end
This test should be successful.
For additional documentation on messages and setting RSpec expectations, see RelishApp's docs on message expectations.
Then, if you wish to test your code within the Resque job itself, you can create a RSpec test for your job. Example:
# spec/lib/jobs/some_job_spec.rb
describe Jobs::SomeJob do
describe "#perform" do
it "should update someone's account" do
...
end
end
end
I'm trying to mock Date.today.wday in a rake task in rspec.
Gem versions: RSpec 2.14.8 --- Rails 4.1.1 --- ruby 2.0.0
Here is a simplified fake version of my test to illustrate essentially what I'm trying to do:
describe "scheduler" do
describe ":thursday_invitations" do
let(:run_issue_invites) do
Rake::Task[:thursday_invitations].reenable
Rake.application.invoke_task :thursday_invitations
end
before do
Rake.application.rake_require 'tasks/scheduler'
Rake::Task.define_task(:environment)
Date.today.should_receive(:wday).and_return(4) ###MY NEMESIS CODE LINE
end
context "on thursday" do
it "issues invitations" do
expect(Date.today.wday).to eq(4) ###THE VERIFICATION TEST THAT KEEPS FAILING
run_issue_invites
expect(<other_stuff_to_test>).to <return_properly>
end
end
end
end
So, the real key of this is mocking out the Date.today.wday. Because I want to be able to run my spec on any day of the week, I need to mock/stub out this method to always return "4" (the day-number for Thursday in Rails). So, I initially setup my test to first verify that it is receiving a "4" (the assertion in the test). If today is, say, Friday (which it is) and I run the test, it fails saying that it expected "4" but got "5". That is, it is not returning the value that I want it to when I receive the method. I have tried stubbing with similar ineffective results. Normally, mocking is a breeze, but what seems to be the hangup is .wday which operates on Date.today.
Because this is a rake task (which I'm not as familiar with mocking), I may have to specify something further, but I haven't been able to get to the bottom of it...
Let me know if you need any other clarifying information.
I believe the reason you're not seeing the behavior you expect is that the object you are mocking is the not the same object under test.
In a Rails 4+ environment, this is what I see on the rails console:
[1]> Date.today.object_id
70104549170200
[2]> Date.today.object_id
70104552970360
The fact that the object_id is different in subsequent calls to Date.today means that each call returns a new object. So Date.today.should_receive(:wday).and_return(4) is setting an expectation on an object that will never be used again.
You'll need to rewrite your spec to ensure the same object is returned by Date.today each time. Here's one solution, omitting other parts of your example for clarity:
let!(:today) { Date.today }
before do
Date.stub(:today).and_return(today)
today.should_receive(:wday).and_return(4)
end
it "issues invitations" do
expect(Date.today.wday).to eq(4)
end