Using shoulda macros with RSpec - ruby-on-rails

I'm trying to use the shoulda macros within RSpec and am having some problems.
I've done the following:
spec_helper.rb:
require 'shoulda/active_record/macros'
Spec::Runner.configure do |config|
...
config.include(Shoulda::ActiveRecord::Macros, :type => :model)
spec/models/foo_spec.rb:
describe Foo do
it { should_have_instance_methods( :save ) } # just for example
end
Which gives me a failure with:
undefined method 'get_options!' for #<Spec::Rails::Example::ModelExampleGroup::Subclass_1:0xb714046c>

just syntax:
not:
it { should_have_instance_methods( :save ) }
but
it { should have_instance_methods( :save ) }
(note the underscores)

I am not 100% sure about the integration with RSpec, but did you wrap the chunk where you call should_have_instance_methods in a context? IIRC all should statements from shoulda require a context wrapped around them.

Related

"Undefined method `build'" when using Rspec + FactoryBotRails

I'm trying this gem called FactoryBotRails. For some reason, when I try it on one of my models unit tests, the following error is thrown.
Failure/Error: my_model = build(:my_model)
NoMethodError:
undefined method `build' for #\<\RSpec::ExampleGroups::MyModel::ValidationTests:0x000055c553959958>
I don't know what I'm doing wrong, as long as I have followed several tutorials on the web, and did the same steps.
Added, in:
gemfile
gem 'factory_bot_rails', '~> 5.1.1'
app/spec/support/factory_bot.rb
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
spec/rails_helper.rb
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
spec/factories/my_models.rb
FactoryBot.define do
factory :my_model do
name { 'some name' }
code { 'some code' }
end
end
And used it like:
my_model = build(:my_model)
What is wrong with my configuration?
The issue might not be what you're calling, but where you're calling it. my_model = build(:my_model) is not syntax you want to use while writing specs, and the error message looks maybe you're calling it from outside of a spec? Because if you're calling it from within a spec, the error should be something along the lines of ArgumentError: Factory not registered: my_model. The spec itself should look like this:
# spec/models/my_model_spec.rb
require 'rails_helper'
describe MyModel do
let(:my_model) { build :my_model }
it { expect(my_model).to be_valid }
end
I would also specify the model name in your factory declaration (i.e., factory :my_model, class: 'MyModel' do). If you want to play with your factories, you can start up a test console:
# start rails console in 'test' environment
rails console test
my_model = FactoryBot.build :my_model
Note that you will need to use FactoryBot.build instead of build in your test console.
If this doesn't resolve your issue, please update your post with the contents of the spec you're trying to run, how you're trying to run it, and expand your definition of your spec/rails_helper.rb file. Since you're new to RSpec, I also suggest checking out http://www.betterspecs.org/ for best practices.
Probably you're missing to setup shortcuts for FactoryGirl by including its methods in your rails_helper:
RSpec.configure do |config|
# ...
config.include FactoryGirl::Syntax::Methods
end
The syntax for creation of factorybot is:
FactoryBot.create :my_model
Pass arguments hash if you need something different:
FactoryBot.create :my_model, name: "John Doe"
For multiple (e.g. 10 my_models):
FactoryBot.create_list :my_model, 10

Rspec-rails overloading describe, breaking existing minitests

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

undefined method 'has_selector?' for rspec decorators

I have the following rspec test:
# spec/decorators/user_decorator_spec.rb
require File.expand_path 'spec/spec_helper'
describe UserDecorator do
let(:user) { UserDecorator.new build(:user, level: build(:level)) }
subject { user }
its(:avatar) { should have_selector 'h6' }
end
and I get error:
Failure/Error: its(:avatar) { should have_selector 'h6' }
NoMethodError:
undefined method `has_selector?' for #<ActiveSupport::SafeBuffer:0x007fecbb2de650>
# ./spec/decorators/user_decorator_spec.rb:7:in `block (2 levels) in <top (required)>'
I have tried the popular suggestion with:
before { ApplicationController.new.set_current_view_context }
but then it says undefined method set_current_view_context. I'm using rspec 2.14.1 and capybara 2.0.1. Also, the strangest thing - when I wrote this test in some helper spec it passed, no problems...
Help...
The simplest fix is probably to add type: 'helper' or `type: 'view' to your describe block:
describe UserDecorator, type: 'helper' do
let(:user) { UserDecorator.new build(:user, level: build(:level)) }
subject { user }
its(:avatar) { should have_selector 'h6' }
end
Doing so will mix ActionView::TestCase::Behavior and Capybara::RSpecMatchers in to your test.
Specs in your specs/helpers directory automatically get the 'helper' type, and specs in specs/views automatically get the 'view' type.
Since specs/decorators is a custom directory that isn't understood by rspec-rails, you need to configure the type manually.
See the RSpec Rails README for more information on the types of tests it supports.
I didn't feel very satisfied about using the type: :view solution proposed in this thread, despite it's a very valid one. The thing is that my spec file for a file inside lib/ directory shouldn't be actually considered as a view spec.
So I did some further research over the internet and found out one that seems a better approach to me.
So considering the original example in this thread. You should just add the following line inside your describe block:
include Webrat::Matchers
So the final example would look like:
# spec/decorators/user_decorator_spec.rb
require File.expand_path 'spec/spec_helper'
describe UserDecorator do
include Webrat::Matchers
let(:user) { UserDecorator.new build(:user, level: build(:level)) }
subject { user }
its(:avatar) { should have_selector 'h6' }
end
has_selector or its alias have_selector is the method of Capybara, not Rspec.
You are using plain Rspec here, so these methods are not possible.
You can use a simple REGEX to check that:
its(:avatar) { should match(/h6.*img/ }

Using ActionView::TestCase::Behavior and the view method in a presenter spec

Using the Railscast example, I have written a spec for my presenter which includes ActionView::TestCase::Behavior and passes in the view method to the presenter.
spec/spec_helper.rb:
...
config.include ActionView::TestCase::Behavior, :example_group => {:file_path => %r{spec/presenters}}
...
spec/presenters/order_presenter_spec.rb:
require 'spec_helper'
describe OrderPresenter do
describe "#subtotal" do
subject { OrderPresenter.new(order, view).subtotal }
let(:order) { stub(:order, working_subtotal: 4500) }
it "renders the subtotal table row" do
should == "<tr><th>SUBTOTAL</th><td>$45.00</td></tr>"
end
end
end
However, this gave me two errors.
The first was
/Users/shevaun/.rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_controller/test_case.rb:12:in `block in <module:TemplateAssertions>': undefined method `setup' for #<Class:0x007fe2343b2f40> (NoMethodError)
so I included ActiveSupport::Testing::SetupAndTeardown in the same way as ActionView::TestCase::Behavior.
Fixing that gave me the error:
NoMethodError:
undefined method `view_context' for nil:NilClass
when calling view. This is caused by the #controller instance variable inside ActionView::TestCase being nil.
I am using Rails 3.2.13 and rspec-rails 2.13.0 and have another application using the same versions which just works.
The only thing I can think of that might make a difference is that this app is using MongoDB so maybe the ActiveRecord application includes something which sets up #controller for free?
I have got a workaround which makes the presenter specs pass, but I would like to know how #controller normally gets instantiated, and if there's a more elegant way to do this for a MongoDB project (if it is ActiveRecord that's doing the magic).
My current solution is to instantiate the #controller instance variable by calling setup_with_controller before the presenter specs.
spec_helper.rb:
RSpec.configure do |config|
config.include ActiveSupport::Testing::SetupAndTeardown, :example_group => {:file_path => %r{spec/presenters}}
config.include ActionView::TestCase::Behavior, :example_group => {:file_path => %r{spec/presenters}}
config.before(:each, example_group: {:file_path => %r{spec/presenters}}) do
setup_with_controller # this is necessary because otherwise #controller is nil, but why?
end
...
end
You can also create your own view:
let(:view) { ActionController::Base.new.view_context }
subject { OrderPresenter.new(order, view).subtotal }
https://www.ruby-forum.com/topic/2922913#1029887

How to write unit test cases for rake tasks in rails?

Hi all I have this situation , I need to write unit test cases for rake tasks in my rails application but i could not figure out a way to do that. Did any one try that ?
What you can do is this..
Write your logic which will run on a rake task inside a model or class.
Write unit test for that model.
Finally call that method inside your rake task.
I found out this link for writing test cases using rspec.
Short and crisp test cases
Basically, create a module which will parse the name of the rake task, and make us available the keyword task, on which we could call expect { task.execute }.to output("your text\n").to_stdout
Here's how you will create the file,
module TaskExampleGroup extend ActiveSupport::Concern
included do
let(:task_name) { self.class.top_level_description.sub(/\Arake /, "") }
let(:tasks) { Rake::Task }
# Make the Rake task available as `task` in your examples:
subject(:task) { tasks[task_name] }
end
end
Add this in the rspec initializer file
RSpec.configure do |config|
# Tag Rake specs with `:task` metadata or put them in the spec/tasks dir
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

Resources