Now I am using thor in a rails project.
I wrote these codes:
lib/tasks/my_task.rb
require 'thor'
module Tasks
class MyTask < Thor
desc 'My Batch', 'This is my awesome batch'
option :date
def execute(type)
# do_something
end
end
end
Tasks::MyTask.start(ARGV)
spec/lib/tasks/my_task_spec.rb
require 'spec_helper'
describe 'Test my task' do
context 'With date option' do
before do
#option = { date: '20150903' }
end
it 'Can insert to db' do
expect do
Tasks::MyTask.new.invoke(:execute, ['commit'], #option)
end.to change(ProductTable, :count).by(1)
end
end
end
The problem is when I run bundle exec rspec, it showed:
Run options: exclude {:heavy=>true}
..................................................................................................................................................................................................****************************...................................................
........................................................************.......................................................................................................................................................................................................******
Commands:
rspec help [COMMAND] # Describe available commands or one specific command
rspec My Batch # This is my awesome batch
.................................................................................................................................................................................................................................................................................
Why the desc messages been shown here? How to config to remove them?
Haven't tested it, but I'd imagine you could probably patch it out with something like:
module Thor
def puts(*args, &block)
# do nothing
end
end
EDIT: actually it looks like this might be handled a little differently here: https://github.com/erikhuda/thor/blob/master/lib/thor/shell/basic.rb
It looks like you could stub out Thor::Shell::Basic#stdout and #stderr
Related
I have a simple test but the describe keyword is not working in Sorbet tests.
The error I'm receiving on these methods:
Method `describe` does not exist on `T.class_of(<root>)`7003
RSpec.describe(Model) do
describe 'my test' do
before(:each) do # .before error
user = FactoryBot.create(:user)
end
it 'can fill in all fields' do # .it errors
end
end
end
I think I need to tell Sorbet some how that this is called in the context of spec_helper.rbbut I'm not sure how to do that.
I've already installed this gem rspec-sorbet and ran
spec/spec_helper.rb
require 'rspec/sorbet'
To silence the errors, I ran this:
RSpec.describe(Model) do
T.bind(self, T.untyped)
# T.bind(self, RSpec) This does not work either
end
I have been trying to test a rake task with RSPEC. So far my understanding of the problem is that DB transactions are creating different contexts for each aspects of the test (one in the test context and one in the rake task execution context). Here are the two files I'm using in my test:
One to make rake tasks available in Rspec and be able to call task.execute:
...spec/support/tasks.rb
require "rake"
module TaskExampleGroup
extend ActiveSupport::Concern
included do
let(:task_name) { self.class.top_level_description.sub(/\Arake /, "") }
let(:tasks) { Rake::Task }
subject(:task) { tasks[task_name] }
end
end
RSpec.configure do |config|
config.define_derived_metadata(:file_path => %r{/spec/tasks/}) do |metadata|
metadata[:type] = :task
end
config.include TaskExampleGroup, type: :task
config.before(:suite) do
Rails.application.load_tasks
end
end
One to properly test my rake task:
...spec/tasks/reengage_spec.rb
require "rails_helper"
describe "rake reengage:unverified_users", type: :task do
let(:confirmed_user){create(:user, :confirmed)}
it "Preloads the Rails environment" do
expect(task.prerequisites).to include "environment"
end
it "Runs gracefully with no users" do
expect { task.execute }.not_to raise_error
end
it "Logs to stdout without errors" do
expect { task.execute }.to_not output.to_stderr
end
it "Reengage confirmed users" do
task.execute
expect(confirmed_user).to have_attributes(:reactivated_at => DateTime.now)
end
end
So far, when the rake task is executed through Rspec, it seems like the user I have created is not available in the context of rake, hence his . reactivated_at remains nil. I though I could use database cleaner to be able to configure DB transactions and preserve the context but I have no idea where to start.
I could end putting all this code in a lib and test it separately but I'm wondering if there is a way to do this. Is there a straightforward way to share the context of execution or to make my FactoryGirl user available when the task is initialized?
I'm using minitest 5.8.4 and rspec-rails 3.5.1. We have a current test suite that's using minitest, but I'm going to slowly migrate us to rspec.
I currently have a lot of tests that are structured like the following:
class UserTest < ActiveSupport::TestCase
describe "a_method" do
it "should return the results" do
assert_a_thing
end
end
end
As soon as I include rspec-rails in my Gemfile, it appears that the describe method is then globally overloaded/taken by RSpec, and when I run rake test it simply skips all of those tests.
Tests that are are in the structure of test 'foo' {it 'works' {}} aren't skipped.
How can I easily make it so that my new RSpec tests, and existing minitests using describe co-exist peacefully?
I think this is because of rspec's monkey patching. In your spec_helper disable monkey patching.
RSpec.configure do |c|
c.disable_monkey_patching!
end
You would then need to have the following in your specs
RSpec.describe "whatever" do
# any describe, scenario, it blocks here don't need the RSpec. prefix
end
Expanding on j-dexx's answer, once you've removed the RSpec version of describe you can add back in the minitest ones with another monkey patch. I've done something like that in this commit for the Chef minitest-handler-cookbook. Mine is base on how minitest/spec looks in 4.7.3, but I'm pretty sure it's similar in the newer minitest.
RSpec.configure { |c| c.disable_monkey_patching! }
[RSpec::Core::DSL.top_level, Module].each do |klass|
klass.class_exec do
# copied from minitest/spec
# https://github.com/seattlerb/minitest/blob/v5.14.4/lib/minitest/spec.rb#L75-L90
def describe desc, *additional_desc, &block # :doc:
stack = Minitest::Spec.describe_stack
name = [stack.last, desc, *additional_desc].compact.join("::")
sclas = stack.last || if Class === self && kind_of?(Minitest::Spec::DSL) then
self
else
Minitest::Spec.spec_type desc, *additional_desc
end
cls = sclas.create name, desc
stack.push cls
cls.class_eval(&block)
stack.pop
cls
end
end
end
Running the following rspec gem versions:
* rspec (2.14.1)
* rspec-core (2.14.7)
* rspec-expectations (2.14.5)
* rspec-mocks (2.14.6)
* rspec-rails (2.14.1)
And octokit 2.7.1
Given the following in spec/support/octokit.rb:
# This support package contains modules for octokit mocking
module OctokitHelper
def mock_pull_requests_for_org org
mock_org_repos(org).map do |repo|
mock_pull_requests repo
end.flatten
end
def mock_org_repos org
##repos ||= [
double('Sawyer::Resource', name: 'repo 1'),
double('Sawyer::Resource', name: 'repo 2')
]
Octokit.stub(:org_repos) { |org=org| ##repos }
##repos
end
def mock_pull_requests repo, gh_handle=nil
##pulls ||= [
double('Sawyer::Resource', title: 'pr 1'),
double('Sawyer::Resource', title: 'pr 2')
]
Octokit.stub(:pull_requests) { |repo| ##pulls }
##pulls
end
end
RSpec.configure do |config|
config.include OctokitHelper
end
Whenever I attempt to call mock_org_repos the first time from a spec, ##repos.first.name is repo 1. The second time around, ##repos.first.name throws a really bizarre error:
1) StudentsController GET #submissions renders pull requests template
Failure/Error: get :submissions, student_id: 2
Double "Sawyer::Resource" received unexpected message :name with (no args)
An example of my usage of mock_org_repos:
require 'spec_helper'
describe StudentsController do
describe 'GET #submissions' do
before do
#user = FactoryGirl.create(:instructor)
#user.stub(:is_instructor?) { true }
#submissions = mock_pull_requests_for_org ENV['GA_ORG_NAME']
controller.stub(:current_user) { #user }
get :submissions, student_id: 2
end
it 'assigns #submissions' do
assigns(:submissions).should == #submissions
end
it 'renders pull requests template' do
response.should render_template 'submissions'
end
end
end
My guess is that one of the doubles you've created in mock_pull_requests with the same name is getting sent :name, but you can check that by making sure your doubles have unique names (i.e. the first argument to double).
Many thanks to rsofaer whom had lent me his second pair of eyes to resolve this, I would like to point out the now very very obvious problem:
Methods stubbed on doubles are 'cleared' every time examples are run, when the stubs are referenced via class variables.
This is a very clear explanation of how RSpec works under the covers.
I found a blog post about Testing Factories First (by BigBinary - which happens to be a Minitest/spec version of Thoughtbot's RSpec original).
Could you please show me the equivalent without the spec framework - just with Minitest (Rails)?
The Thoughtbot approach (RSpec)
spec/factories_spec.rb
FactoryGirl.factories.map(&:name).each do |factory_name|
describe "The #{factory_name} factory" do
it 'is valid' do
build(factory_name).should be_valid
end
end
end
Rakefile
if defined?(RSpec)
desc 'Run factory specs.'
RSpec::Core::RakeTask.new(:factory_specs) do |t|
t.pattern = './spec/factories_spec.rb'
end
end
task spec: :factory_specs
The BigBinary approach (Minitest, spec)
spec/factories_spec.rb
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe FactoryGirl do
EXCEPTIONS = %w(base_address base_batch bad_shipping_address)
FactoryGirl.factories.each do |factory|
next if EXCEPTIONS.include?(factory.name.to_s)
describe "The #{factory.name} factory" do
it 'is valid' do
instance = build(factory.name)
instance.must_be :valid?
end
end
end
end
lib/tasks/factory.rake
desc 'Run factory specs.'
Rake::TestTask.new(:factory_specs) do |t|
t.pattern = './spec/factories_spec.rb'
end
task test: :factory_specs
What is the Minitest equivalent (without spec)?
The approach I am presenting below is slightly different than the two original solutions - in the sense that my approach creates only one test, within which I cycle through the factories and run an assertion against each. I was not able to create a solution that mimics the original solutions any closer - which is (I believe) a separate test method for each factory. If someone could show such an implementation, that would be cool.
test/aaa_factories_tests/factories_test.rb
require File.expand_path(File.dirname(__FILE__) + '/../test_helper.rb')
class FactoriesTest < Minitest::Unit::TestCase
puts "\n*** Factories Test ***\n\n"
EXCEPTIONS = %w(name_of_a_factory_to_skip another_one_to_skip)
def test_factories
FactoryGirl.factories.each do |factory|
next if EXCEPTIONS.include?(factory.name.to_s)
instance = FactoryGirl.build(factory.name)
assert instance.valid?, "invalid factory: #{factory.name}, error messages: #{instance.errors.messages.inspect}"
instance = factory = nil
end
end
end
Thanks to the way Minitest works out of the box -- add any directories under test/ and minitest-rails will automatically create the associated rake task for it. So let's say you add a test/api/ directory, rake minitest:api will automagically be available. -- I see the task when I run bundle exec rake -T with no other configurations:
rake minitest:aaa_factories_tests # Runs tests under test/aaa_factories_tests
And I am able to run this task successfully:
-bash> bundle exec rake minitest:aaa_factories_tests
*** Factories Test ***
Run options: --seed 19208
# Running tests:
.
Finished tests in 0.312244s, 3.2026 tests/s, 9.6079 assertions/s.
1 tests, 3 assertions, 0 failures, 0 errors, 0 skips
Despite the ugliness of prepending the directory with aaa, I am able to have the factories tested first with:
bundle exec rake minitest:all
The reason for the aaa prepend solution is MiniTest does a Dir glob and on Mac OS X (and other Unix variants) the results are sorted alphabetically (though the results differ across different platforms).
As well, I prepended the default_tasks array with aaa_factories_tests to have the factories tested first in the default Minitest task (i.e. when running bundle exec rake minitest).
lib/tasks/factories_first.rake
MiniTest::Rails::Testing.default_tasks.unshift('aaa_factories_tests') if Rails.env =~ /^(development|test)\z/
Note that the above condition avoids erroneously referencing Minitest in environments where it is unavailable (I have confined minitest-rails to :test and :development groups in Gemfile). Without this if-condition, pushing to Heroku (for example to staging or production) will result in uninitialized constant MiniTest.
Of course I am also able to run the factories test directly:
bundle exec ruby -I test test/aaa_factories_tests/factories_test.rb
Here is a solution for MiniTest without the spec framework:
test/factories_test.rb
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
class FactoriesTest < ActiveSupport::TestCase
EXCEPTIONS = %w(griddler_email)
FactoryBot.factories.map(&:name).each do |factory_name|
next if factory_name.to_s.in?(EXCEPTIONS)
context "The #{factory_name} factory" do
should 'be valid' do
factory = build(factory_name)
assert_equal true, factory.valid?, factory.errors.full_messages
end
end
end
end
lib/tasks/factory.rake
namespace :test do
desc 'Test factories'
Rake::TestTask.new(:factories) do |t|
t.pattern = './test/factories_test.rb'
end
end
task minitest: 'test:factories'
The most important thing is to use taks minitest instead of task test if you want the factories tests to be run before other tests.