"Undefined method `build'" when using Rspec + FactoryBotRails - ruby-on-rails

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

Related

How to configure Sorbet with rspec?

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

how to test a gem's file

I have a gem inside my Rails project, really simple, it translate an ip address to a real address (e.g. Gem.locate '127.0.0.1' returns { country: 'de' }
The code is like this
ip_locator/lib/service.rb
module IpLocator
class Service
include Singleton
attr_reader :db
def initialize
#db = MaxMindDB.new('db/GeoLite2-City.mmdb')
end
def locate(ip)
db.lookup ip
end
end
end
when I'm in the rails project, I can properly do this: IpLocator::Service.instance.locate '74.125.225.224' and get the information I need.
Now I'm trying add some tests to the gem (not in the rails project).
require 'spec_helper'
RSpec.describe IpLocator do
describe 'ip_locator' do
context 'when the data exists' do
let(:ip) { '74.125.225.224' }
subject do
IpLocator::Service.instance.locate ip
end
it 'return country data' do
expect(subject.found).to be_truthy
end
end
end
end
but when I run the test, I get
1) IpLocator ip_locator when the data exists return country data
Failure/Error: IpLocator::Service.instance.locate ip
NameError:
uninitialized constant IpLocator::Service
I already tried to change IpLocator::Service to Service, I tried require ip_locator inside the spec file (although it is already required inside spec_helper.rb). I'm pretty sure I'm doing something really stupid but not sure what exactly
Here is my spec_helper.rb
require 'bundler/setup'
Bundler.setup
require 'byebug'
require 'ip_locator'
Dir[File.dirname(__FILE__) + '/lib/*.rb'].each { |file| require file }
RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = '.rspec_status'
# Disable RSpec exposing methods globally on `Module` and `main`
config.disable_monkey_patching!
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
and I'm seeing the error:
NameError: uninitialized constant IpLocator::Service::Singleton
Did you mean? SignalException
then, if I try to require singleton as well in the spec_helper, I get
1) IpLocator::Service ip_locator when the data exists return country data
Failure/Error: #db = ::MaxMindDB.new('db/GeoLite2-City.mmdb')
NameError:
uninitialized constant MaxMindDB
I'm getting crazy

Rspec - How to create macros with tags?

Is there a way to run Rspec macros conditionally ?
For example ability to filter macros using
RSpec.configure do |c|
c.filter_run_excluding :broken => true
end
## This should get skipped
it_should_validate_with_macro :some_param, :broken => true
Note: This is to invoke group of tests dynamically. So pending kinda solution is not I'm looking for.
describe "an example" do
it "is implemented but waiting" do
pending("something else getting finished")
this_should_not_get_executed
end
end
from https://relishapp.com/rspec/rspec-core/v/2-0/docs/pending/pending-examples
You can run specs in the same process, and you can also do things like capture the output.
But a simple example that might work for you is simply to create a ruby script:
require 'rspec/core'
RSpec.configuration.add_setting(:some_setting)
RSpec.configuration.some_setting = false
RSpec::Core::Runner.run(['spec/models/bar.rb'])
RSpec.clear_examples
if RSpec.configuration.some_setting
RSpec::Core::Runner.run(['spec/models/foo.rb'])
end
Then, in your rspec script that will modify the setting:
RSpec.describe 'bar' do
it 'bar' do
RSpec.configuration.some_setting = true
end
end
This will conditionally run the spec in foo.rb.

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

Using shoulda macros with RSpec

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.

Resources