hackaton slides: link
meetup slides: link
meetup video: link
Sidekiq::Portal - scheduled jobs runner for your test environments,
which execution process must occur during the Timecop.travel(...) operations according to the scheduler config.
Each job starts at the time it was supposed to start according to the scheduler plan -
the internal Time.current expression will give you exactly the scheduler-planned time.
Supports ActiveJob backend (Sidekiq::Worker coming soon). Works with sidekiq-scheduler-based job configs (sidekiq-cron coming soon).
Realized as an instance, but have a global-based implementation too.
Table of contents
- Installation
- Configuration
- Example
- Roadmap
- License
- Authors
Installation
- in your
Gemfile
gem 'sidekiq', '>= 5' # runtime dependency
gem 'timecop', '~> 0.9' # runtime dependency
group :test do
gem 'rspec'
gem 'sidekiq_portal'
end- run shell command:
bundle install-
spec_helper.rb:
require 'timecop' # runtime dependency
require 'sidekiq' # runtime dependency
require 'sidekiq/api'
require 'sidekiq/testing'
require 'sidekiq_portal'Configuration
-
default_timezone- global time zone for your jobs (UTCby default); -
retry_count- Sidekiq's built-in retry mechanism simulation (0by default); -
retry_on- retry only on a set of exceptions ([StandardError]by default); -
scheduler_config-sidekiq-scheduler-based scheduler configuration ({}by default (non-configured)); -
Sidekiq::Portal.reload!(&configuration)- reload portal configurations;
In your spec_helper.rb:
# portal configuration
Sidekiq::Portal.setup! do |config|
config.default_timezone = 'UTC' # 'UTC' by default
config.retry_count = 3 # 0 by default
config.retry_on = [StandardError] # [StandardError] by default
# pre-defined sidekiq-scheduler configs (Rails example)
config.scheduler_config = Rails.application.config_for(:sidekiq)[:schedule]
# manual sidekiq-scheduler configs
config.scheduler_config = {
LoolJob: { every: '15m' },
kek_job: { cron: '0 * * * * *', class: :KekJob }
}
end
# global state clearing logic
RSpec.configure do |config|
config.before { Sidekiq::Worker.clear_all }
config.after { Timecop.return }
config.after { Sidekiq::Portal.reload! }
endAnd in your tests:
RSpec.describe 'Some spec' do
specify 'magic?' do
Timecop.travel(Time.current + 2.hours) # magic begins here 😈
end
endExample
- Job class:
class HookExampleJob < ApplicationJob
def perform
GLOBAL_HOOK_INTERCEPTOR << Time.current # intercept current time
end
end-
Sidekiq::Schedulerconfig:
:schedule:
HookExample:
every: '15m'-
HookExampleJobspec:
RSpec.describe 'HookExampleJob sheduler plan' do
specify 'scheduled?' do
stub_const('GLOBAL_HOOK_INTERCEPTOR', [])
expect(GLOBAL_HOOK_INTERCEPTOR.count).to eq(0) # => true
# do some magic 😈
Timecop.travel(Time.current + 2.hours)
expect(GLOBAL_HOOK_INTERCEPTOR.count).to eq(8) # => true (😈 magic)
puts GLOBAL_HOOK_INTERCEPTOR # 😈
# => outputs:
# 2019-12-24 03:05:39 +0300 (+15m) (Time.current from HookExampleJob#perform)
# 2019-12-24 03:20:39 +0300 (+15m)
# 2019-12-24 03:35:39 +0300 (+15m)
# 2019-12-24 03:50:39 +0300 (+15m)
# 2019-12-24 04:05:39 +0300 (+15m)
# 2019-12-24 04:20:39 +0300 (+15m)
# 2019-12-24 04:35:39 +0300 (+15m)
# 2019-12-24 04:50:39 +0300 (+15m)
end
endRoadmap
-
Sidekiq::Testing.portal!test mode with support for:inlineand:fake; (Sidekiq::Testing.inline!andSidekiq::Testing.fakerespectively); - support for
ActiveSupport::TimeZoneinstances indefault_timezoneconfig; - rspec matchers;
-
#reload!should use previously defined settings?; - support for
Sidekiq::Workerjob backend; - support for
Sidekiq::Cronscheduler plans; - more specs;
- documentation and examples for instance-based portals (
Sidekiq::Portal.new(&configuration)); - configurable job execution randomization (for jobs which should be invoked at the same time) (randomized invocation and not - at the same time or not);
- configurable in-line invocations (with job list config);
- configurable and conditional portal invocation (run over all specs or only over the one or etc) (suitable for unit tests);
- Time as external dependency;
- getting rid of ActiveSupport's Time-related core extensions;
- better specs;
- 100% test coverage;
License
Released under MIT License.