I've this file called jobs.rb
require File.expand_path("../environment", __FILE__)
job "do.stuff" do |args|
$value = 10
end
Then I've this spec file called jobs_spec.rb
require File.expand_path("../../spec_helper", __FILE__)
describe "some beanstalk jobs" do
it "should work" do
# What to do here?
$value.should eq(10)
end
end
How do I test the $value variable?
You have to run beanstalkd on the machine you are running the test on.
require "Stalker"
describe Stalker, "job" do
before :all do
# Make sure beanstalkd is running
if `pgrep beanstalkd` == ""
raise "PRECONDITION NOT MET: beanstalkd not running"
end
module Stalker
def log(msg); end
def log_error(msg); end
end
end
before :each do
Stalker.clear!
$result = -1
$handled = false
end
it "enqueue and work a job" do
val = rand(999999)
Stalker.job('my.job') { |args| $result = args['val'] }
Stalker.enqueue('my.job', :val => val)
Stalker.prep
Stalker.work_one_job
val.should == $result
end
end
I'd check out how the library itself runs it's test:
https://github.com/adamwiggins/stalker/blob/master/test/stalker_test.rb
Keep in mind that this is using Test::Unit, however you can apply the same techniques in Rspec.
Related
here a small description of my code (simplified)
app/jobs/
class GenerateInvoiceJob < ActiveJob::Base
queue_as :default
def perform()
Invoice.create
end
end
app/models/
class Product < ActiveRecord::Base
def buy
GenerateInvoiceJob.perform_later
end
end
spec/jobs
RSpec.describe AnotherJob, type: :job do
context "with filter" do
...
end
end
spec/models
RSpec.describe Product, type: :model do
describe '#buy' do
it "should generate invoice" do
Product.create().buy
expect(Invoice.all.size).to eq 1
end
end
end
with rails 4.2.11
when I run
rspec spec/models/product_spec.rb
then the test is ok (the job is performed)
when I run
rspec spec -e 'should generate invoice'
then the test fail cause the job is not performed
if I delete all test jobs from spec/jobs and then run
rspec spec -e 'should generate invoice'
then the test is ok (the job is performed)
I can't understand why having some tests for jobs prevents other jobs to perform ? Is there a solution for this?
with rails 5 and rails 6
whatever I do, the test always failed as the job is never performed ?
Aren't jobs performed anymore during tests since rails 5 ?
thanks for help
update 1 after first answer :
thanks a lot for your answer
just to be sure I do correctly :
I added in environment/test.rb
config.active_job.queue_adapter = :test
and in my spec/models/product_spec.rb
RSpec.describe Product, type: :model do
describe '#buy' do
it "should generate invoice" do
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
Product.create().buy
expect(Invoice.all.size).to eq 1
end
end
end
not sure I put
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
at the good place ?!
You need to set:
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
However, using have_enqueued_job is a more common approach.
EDIT: There's even an easier way that slipped my mind:
ActiveJob::Base.queue_adapter = :inline
you need to wrap your code in perform_enqueued_jobs
it 'test request' do
perform_enqueued_jobs do
expect(request).to be_success
end
end
I'm doing a test using RSPEC, and used Sidekiq for background jobs.
Since, there's no generator for workers in rspec, not sure what to use.
https://relishapp.com/rspec/rspec-rails/docs/generators
require 'spec_helper'
RSpec.describe TestWorker, type: ? do # ex. :worker :sidekiq ...
describe "TestWorker" do
it "" do
....
end
end
end
bundle exec rspec spec/workers/test_worker_spec.rb
Doing like below, i'm getting: uninitialized constant TestWorker
require 'spec_helper'
describe TestWorker do
it "" do
....
end
end
As i tried, gem rspec-sidekiq
https://github.com/philostler/rspec-sidekiq
Can someone provide a sample template for testing app/workers/ in Rspec.
Thanks.
I have not used the rspec-sidekiq gem, however, here is an example of how I am checking for Background jobs which uses sidekiq
# app/spec/workers/demo_worker_spec.rb
require 'rails_helper'
require 'sidekiq/testing'
Sidekiq::Testing.fake!
RSpec.describe DemoWorker, type: :worker do
describe "Sidekiq Worker" do
let (:demo) { FactoryGirl.create(:demo) }
it "should respond to #perform" do
expect(DemoWorker.new).to respond_to(:perform)
end
describe "Demo" do
before do
Sidekiq::Extensions.enable_delay!
Sidekiq::Worker.clear_all
end
it "should enqueue a Email and SMS job" do
assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
Mailer.delay.demo_request(demo.id)
assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
end
end
end
end
As of I'm checking, if the instance responds to perform.
Then, I'm asserting before and after the job is scheduled.
You might sometimes want to test more than just the fact that the worker has been enqueud or not.
While it is better to decouple complex stuff that could happen in a worker's perform block, it can be tested as a standard class :
it { expect { MyWorker.new.perform }.to change { ...expectations... } }
or
it do
MyWorker.new.perform
... expectations ..
end
i have task /lib/crawler.rake like that:
namespace :crawler do
area_names = Dir[Rails.root.join("lib", "crawler", "*.rb")].map do |file_name|
File.basename(file_name, ".rb")
end
area_names.each do |area_name|
task area_name.to_sym => :environment do
logger = Logger.new("log/crawl_#{area_name}.log")
# do something
parallel_results = crawler.crawl
mutex = Mutex.new
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
begin
# do something
rescue => e
# do something
raise e
end
end
Availability.update_by_grounds_and_time
end
end
end
Logic here, if everything's ok after parallel, we'll call update_by_grounds_and_time method to update Availability; if get error, we'll stop action and raise error.
So i want to write rspec to test for these cases, i want to mock/stub output of task here (pass or raise error) and check did we call update_by_grounds_and_time method?
Can we don't need invoke really task? can we use Rspec Mock?
Can you help me!
Thank
What I usually do in these cases is I extract the meat into a separate class/service-object/whatever, which is much easier to test. The rake task then becomes just an invoker of that object and, as such, doesn't need to be tested.
If it is defined in Rakefile, try this:
require 'rake'
RSpec.describe "Rake Tasks" do
before do
file, path = Rake.application.find_rakefile_location
Rake.load_rakefile("#{path}/#{file}")
end
it "should invoke some tasks" do
expect(Availability).to receive(:update_by_grounds_and_time)
Rake.application["crawler:#{area_name}"].invoke
end
end
If it is defined in foo.rake, then try this one:
require 'rake'
RSpec.describe "Rake Tasks" do
before do
Rake.application.rake_require('/path/to/lib/tasks/foo')
end
it "should invoke some tasks" do
expect(Availability).to receive(:update_by_grounds_and_time)
Rake.application["crawler:#{area_name}"].invoke
end
end
UPDATE (error case)
For example
# foo.rake
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
begin
foo = Foo.new
foo.bar
# do something else
rescue => e
# do something
raise e
end
end
# foo_spec.rb
require 'rake'
RSpec.describe "Rake Tasks" do
before do
Rake.application.rake_require('/path/to/lib/tasks/foo')
end
it "should not call Availability#update_by_grounds_and_time if error raised" do
allow_any_instance_of(Foo).to receive(:bar).and_raise(StandardError)
expect(Availability).to_not receive(:update_by_grounds_and_time)
expect { Rake.application["crawler:#{area_name}"].invoke }.to raise_error(StandardError)
end
end
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
In my helper module, I have:
def abc(url)
...
if request.env['HTTP_USER_AGENT']
do something
end
end
In my spec file, I have:
describe "#abc" do
before(:each) do
#meth = :abc
helper.request.env['HTTP_USER_AGENT'] = "..."
end
it "should return the webstart jnlp file" do
#obj.send(#meth, "some_url").should ....
end
end
When I run the spec I have this error:
undefined local variable or method `request' for <ObjectWithDocHelperMixedIn:0x00000103b5a7d0>
How do I stub for request.env['...'] in my specs?
Thanks.
If you're using rspec-rails, you might be able to use controller.request in your helper tests.
Well, you've almost nothing to do:
before(:each) do
#meth = :abc
request.env['HTTP_USER_AGENT'] = "..."
end
I just gave this another try and this passes:
#in helper
def foo
request.env['HTTP_USER_AGENT']
end
#spec
it "foo" do
helper.request.env['HTTP_USER_AGENT'] = 'foo'
expect(helper.foo).to eq 'foo'
end
You can override user-agent set in the request env by doing the following.
before(:each) do
#meth = :abc
helper.request.user_agent = 'something else'
end
Then, in your spec:
it "does stuff" do
expect(helper.send(#meth, "some_url")).to # ...
end
for those who use request specs instead of controller specs and want to set request.env can do it like this:
Rails.application.env_config["whatever"] = "whatever"
this will make request.env["whatever"] available in your controllers with value you gave it in your specs.
Try this:
stub(request).env { {"HTTP_USER_AGENT" => "Some String"} }