Testing Background Jobs in Rails

Testing Background Jobs in Rails

2022, May 01    

In my last post I set up a background job implementation in a Rails application, you can read that here. For testing these changes, we will need to make adjustments to the User model, and add in a new test for the UserLogCreateJob class.

User Model Test

Since the addition of a callback method in the user model (after_create), the test will need to account for enqueuing a job once a new user is created. First add the rspec-rails gem to the Gemfile, afterwards be sure to run bundle install. To finish setting up RSpec, run the command rails g rspec:install. That will create a /spec folder with spec_helper & rails_helper ruby files. Create a basic test file for our user under the spec directory, in this file “spec/models/user_spec.rb”.

require "rails_helper"

RSpec.describe User, type: :model do

    after(:all) do
        User.destroy_all
        UserLog.destroy_all
    end

    context "Relationships" do
        it "Has User Logs After Create" do
            user = User.create

            expect(user.user_logs.size).to eq(1)
            expect(user.user_logs.first).to be_an_instance_of(UserLog)
            expect(user.user_logs.first.user_id).to eq(user.id)
        end
    end
end

Running the test as-is will fail, due to the job not being processed yet so the UserLog has not been created. We can see 0 returning for the user_logs size in the stack trace:

Failure/Error: expect(user.user_logs.size).to eq(1)

       expected: 1
            got: 0

To fix this, we will need to add in test helper methods. Including ActiveJob::TestHelper within the test will give us access to certain methods that will make it possible to process the enqueued job. See more methods available here. Adding in the method #perform_enqueued_jobs will process the job we need for this test to pass. With these changes the test will now pass:

require "rails_helper"

RSpec.describe User, type: :model do
    include ActiveJob::TestHelper

    after(:all) do
        User.destroy_all
        UserLog.destroy_all
    end

    context "Relationships" do
        it "Has User Logs After Create" do
            user = User.create
            perform_enqueued_jobs

            expect(user.user_logs.size).to eq(1)
            expect(user.user_logs.first).to be_an_instance_of(UserLog)
            expect(user.user_logs.first.user_id).to eq(user.id)
        end
    end
end

In addition to a model test, I’m going to create a specific test for our job in spec/jobs/user_log_create_job_spec.rb. After instantiating a new User, we can assert that a job to create a new user_log record has been enqueued. A basic test setup using User & UserLog models will look something like this:

require "rails_helper"

RSpec.describe UserLogCreateJob do
    include ActiveJob::TestHelper
    after(:each) do
        User.destroy_all
        UserLog.destroy_all
    end

    context "UserLogCreateJob" do
        it "Is enqueued successfully" do
            assert_enqueued_with(job: UserLogCreateJob) do
                User.create
            end
        end
    end
end

To really see how this is working, we could add in a few more assertions. If we add in assert_no_enqueued_jobs before the current assertion in the test, it will still pass. There should only be an enqueued job once the User has been created, within the block of the second assertion. We could assert that once the User has been created that we should expect there to be a job in the queue, and after performing those jobs that we should be left with no enqueued jobs.

require "rails_helper"

RSpec.describe UserLogCreateJob do
    include ActiveJob::TestHelper
    after(:each) do
        User.destroy_all
        UserLog.destroy_all
    end

    context "UserLogCreateJob" do
        it "Is enqueued successfully" do
            assert_no_enqueued_jobs
            assert_enqueued_with(job: UserLogCreateJob) do
                User.create
            end
            assert_enqueued_jobs 1

            perform_enqueued_jobs
            assert_no_enqueued_jobs
        end
    end
end

References:

ActiveJob Testing