Rails 4 Use Factory Girl factories from Engine - ruby-on-rails

I've created a rails engine (full, not mountable) to provide models to a number of different rails apps. I use Factory Girl Rails to test this engine and the tests all run fine for the engine itself.
I now want to be able to use these factories in other apps that include this engine.
The dependencies for the Gemspec look like this:
s.add_dependency "rails", "~> 4.0.3"
s.add_dependency "mysql2", "~> 0.3.15"
s.add_development_dependency "rspec-rails", "~> 3.0.0.beta"
s.add_development_dependency "factory_girl_rails", "~> 4.4.1"
s.add_development_dependency "shoulda-matchers", "~> 2.5.0"
And i have defined my factories in /spec/factories.rb:
factory :user do
...
end
To add the factories.rb to the definition paths in factory girl, I added the following to my /lib/engine_name/engine.rb file:
class Engine < ::Rails::Engine
initializer "model_core.factories", :after => "factory_girl.set_factory_paths" do
FactoryGirl.definition_file_paths << File.expand_path('../../../spec/factories.rb', __FILE__) if defined?(FactoryGirl)
end
end
In my rails apps I include the engine by adding the following to the Gemfile:
gem 'engine_name', git: "<GIT_LOCATION>"
I also add factory_girl_rails to the app (is there a way I can expose this from the engine? rather than having to specify it in the apps Gemfile too?).
And require factory girl rails in spec_helper.rb:
require 'factory_girl_rails'
Now when I write, say, a controller test like the following:
it "saves the user to the database" do
expect{post :create, user: attributes_for(:user)}.to change{User.count}.by(1)
end
I get the error: "Factory not registered: user"
I've double checked the factory girl definition file paths by opening the ruby console and running FactoryGirl.definition_file_paths and i can see the factories.rb from the engine in the output: "/home/ ... /gems/engine-name-abc123/spec/factories.rb"
Is there anything else i need to do to make these factories available?
(I have found a few similar questions on stackoverflow and beyond that all seem to point to adding those lines in engine.rb, or specifying namespaces in the factories.rb but I am not using namespaces with this engine.)

I found the easiest route to take with this was to add an install generator that simply copies the factories over. I also have the generator run the install migrations rake task as I will need these in any apps that use the engine.
So, in lib/generators/my_engine/install/install_generator.rb:
module MyEngine
module Generators
class InstallGenerator < Rails::Generators::Base
source_root File.expand_path('../templates', __FILE__)
def copy_migrations
rake("my_engine:install:migrations")
end
def copy_factories
copy_file "../path/to/spec/factories.rb", "spec/factories.rb"
end
end
end
end
Now in projects that use this engine, I simply run rails generate my_engine:install and the factories (and migrations) are ready for me to use.

Related

How do I create a command line rails generator gem?

I know how to create a rails generator gem which is called like:
rails g my_generator command
And I know how to create a thor gem which can be called like:
my_generator command
But I don't know how to create a rails generator that can be called using an executable. I have tried by creating a lib/my_generator/cli.rb file like:
require 'thor'
module Mang
class Cli < Thor
include ::Rails::Generators::Base
desc "install_gem", "install a gem"
def install_gem
gem 'thor', "0.18.1"
end
end
end
But I get the following error despite having added Rails as a dependency in my gemspec.
uninitialized constant Rails (NameError)
The fix was just a matter of including the rails/generators/actions module.
require 'thor'
require 'rails/generators'
require 'rails/generators/actions'
module Mang
class Cli < Thor
include Thor::Actions
include Rails::Generators::Actions
desc "install_gem", "install a gem"
def install_gem
gem 'thor', "0.18.1"
end
end
end

FactoryGirl creating objects in development environment

When I boot up my rails console in development I see FactoryGirl creating objects. Clearly I'm doing it wrong, but what's the right way to do this? This code makes my tests work...
# tests/factories/board.rb
FactoryGirl.define do
factory :word do
sequence(:text) { |n| "FAKETEXT#{n}" }
end
factory :board do
trait :has_words do
words [
FactoryGirl.create(:word, id: "514b81cae14cfa78f335e250"),
FactoryGirl.create(:word, id: "514b81cae14cfa7917e443f0"),
FactoryGirl.create(:word, id: "514b81cae14cfa79182407a2"),
FactoryGirl.create(:word, id: "514b81cae14cfa78f581c534")
]
end
end
end
Note there's no mention of factory anything in any file in my config directory, so whatever loading is happening automatically by the gem. The relevant part of my Gemfile reads:
# Stuff not to use in production
group :development, :test do
# Command-line debugger for development
gem "debugger"
# for unit testing - replace fixtures
gem "factory_girl_rails"
end
So I could just take factory girl out of the development environment. But I think the fact that these records are being created before the factory is being used is a sign that I've written my factory incorrectly. But if you tell me the factory is written correctly, I'll just do that.
Had the same problem. There are 2 ways to fix this,
1. Use FactoryGirl syntax to reference Factory within a Factory.
Replace FacotryGirl.create(:my_factory) with factory: :my_factory
More info on this, https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations
2. factory_girl :require => false in Gemfile
This causes Factories to generate objects on boot,
group :development, :test do
gem 'factory_girl_rails'
end
Why? During Rails boot, Bundler requires all the gems in the development group, and it seems that FactoryGirl requires all their factory files. Requiring the factories evaluates the Ruby code, and thus, FactoryGirl.create(:my_factory) gets called.
This can be fixed by,
# Gemfile
group :development, :test do
gem 'factory_girl_rails', :require => false
end
Just make sure to manually require factory_girl in your test environment, EG
# spec_helper
require 'factory_girl'
You just need to move factory girl out of the development environment.
I had the same issue, so I just did
group :test do
gem 'faker'
gem 'factory_girl_rails'
end
And works like a charm.
I'm not using those gems at all in development so its correct to just define them in test.
for each factories or model, you have to put in different file
spec/factories/word_factory.rb
spec/factories/board_factory.rb
So the content of each factory, you can do something similar to this:
FactoryGirl.define do
factory :board do
word
special_id
end
end
when in your test folder such as models/board_spec.rb
you can create your object
let(:word) { FactoryGirl.create(:word)
let(:board) { FactoryGirl.create(:board, word: word) }
nt sure is this what you need,correct me if I'm wrong
Assuming you want the words created when the factory is invoked and not at Rails startup, then you need to place the array of words inside a block, to wit:
trait :has_words do
words do [
FactoryGirl.create(:word, id: "514b81cae14cfa78f335e250"),
FactoryGirl.create(:word, id: "514b81cae14cfa7917e443f0"),
FactoryGirl.create(:word, id: "514b81cae14cfa79182407a2"),
FactoryGirl.create(:word, id: "514b81cae14cfa78f581c534")
]
end
end

How to run single test class using minitest in ROR

My Gemfile looks like this:-
group :test do
# Pretty printed test output
gem 'capybara'#,'1.1.2'
gem 'cucumber-rails','1.2.1'
gem 'cucumber','1.1.4'
gem 'rspec-rails','2.8.1'
gem 'rspec-cells','0.1.2'
gem "factory_girl_rails"
gem "guard-rspec"
gem "minitest"
gem 'headless'
gem 'minitest-rails'
gem 'minitest-rails-capybara'
end
minitest_helper.rb looks like :-
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
class ControllerTest < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
register_spec_type(/integration$/, self)
end
And my products_controller_test.rb looks like this:-
require "minitest_helper"
describe "Products Controller" do
it "shows product's name" do
uname="Glasses"
product1 = Product.create!(:name => uname, :description => uname, :no_of_items => 3,:fee_percentage => 4)
visit products_path
page.text.must_include "Glasses"
end
end
BUT..after executing ruby -Itest test/controllers/products_controller_test.rb
I get no error,no indication to show that this test class has been loaded :-
ruby -Itest test/controllers/products_controller_test.rb
:public is no longer used to avoid overloading Module#public, use :public_folder instead
from /usr/local/rvm/gems/ruby-1.9.2-p290/gems/resque-1.19.0/lib/resque/server.rb:12:in `<class:Server>'
Loaded suite test/controllers/products_controller_test
Started
Finished in 0.004953 seconds.
0 tests, 0 assertions, 0 failures, 0 errors, 0 skips
its first time i am using Minitest...
Your Gemfile is a little heavy... If you remove all the RSpec references, you'll run just fine.
(The "describe" and "it" methods are being usurped by rspec)
Remove:
gem 'rspec-rails','2.8.1'
gem 'rspec-cells','0.1.2'

Factory already registered: user (FactoryGirl::DuplicateDefinitionError)

Description of problem:
- I've setup factory_girl_rails however whenever I try and load a factory it's trying to load it multiple times.
Environment:
- rails (3.2.1)
- factory_girl (2.5.2)
- factory_girl_rails (1.6.0)
- ruby-1.9.3-p0 [ x86_64 ]
> rake spec --trace
** Execute environment
-- Creating User Factory
-- Creating User Factory
rake aborted!
Factory already registered: user
The only other thing I've changed is:
/config/initializers/generator.rb
Rails.application.config.generators do |g|
g.test_framework = :rspec
g.fixture_replacement :factory_girl
end
GEMFILE
gem 'rails', '3.2.1'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
gem 'devise'
gem 'haml-rails'
group :development do
gem 'hpricot'
gem 'ruby_parser'
gem "rspec-rails"
end
group :test do
gem "rspec"
gem 'factory_girl_rails'
end
gem 'refinerycms-core', :git => 'git://github.com/resolve/refinerycms.git'
gem 'refinerycms-dashboard', :git => 'git://github.com/resolve/refinerycms.git'
gem 'refinerycms-images', :git => 'git://github.com/resolve/refinerycms.git'
gem 'refinerycms-pages', :git => 'git://github.com/resolve/refinerycms.git'
gem 'refinerycms-resources', :git => 'git://github.com/resolve/refinerycms.git'
gem 'refinerycms-settings', :git => 'git://github.com/resolve/refinerycms.git'
group :development, :test do
gem 'refinerycms-testing', :git => 'git://github.com/resolve/refinerycms.git'
end
gem 'refinerycms-inventories', :path => 'vendor/engines'
FactoryGirl.define do
factory :role do
title "MyString"
end
end
This seems to be a compatibility/environment issue that I can't seem to figure out. Any suggestions?
EDIT: here's my spec/spec_helper.rb:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
#require 'factory_girl_rails'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
### Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
#config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
end
The gem factory_girl_rails should be required in the spec_helper.rb rather than the gemfile - it is possible that you are requiring FactoryGirl twice which is why you are getting the duplicate.
Try this in your gem file:
group :test do
gem "rspec"
gem 'factory_girl_rails', :require => false
end
Then make sure that factory girl is required in the spec_helper with:
require 'factory_girl_rails'
By the way - you don't need both rspec and rpsec-rails in your gemfile. You can replace both with the following:
group :development, :test do
gem 'rspec-rails'
end
You need rspec in both groups so that the rake tasks will work in development and the core testing will work in test.
I had the same problem recently. In my case one of the files in /factories had a _spec.rb ending (result of creative cp use). It was loading twice, first by rspec and then as a factory.
Is there any chance you pasted this whole snippet for the support file from the config docs?
# RSpec
# spec/support/factory_girl.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
# RSpec without Rails
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.before(:suite) do
FactoryGirl.find_definitions
end
end
If you read the comments you'll see you only want one block or the other. I made this mistake and got the error stated in the question.
I had this problem too. In my case there were two files with the same code, like this:
FactoryGirl.define do
factory :user do
end
end
One file was named "Useres.rb" and the other "User.rb" so I just deleted "Useres.rb" and fixed the error.
Call FactoryGirl.define(:user) or FactoryGirl.find_definitions twice you also have this problem.
Try removing the second call or:
FactoryGirl.factories.clear
FactoryGirl.find_definitions
Another possible reason is spare call of FactoryGirl.find_definitions.
Try to remove find_definitions if found.
Make sure your individual factory files are not ending with _spec.
https://github.com/thoughtbot/factory_girl/issues/638
Loading factory girl into a development console will do this too:
require 'factory_girl_rails'; reload!; FactoryGirl.factories.clear; FactoryGirl.find_definitions
will raise a FactoryGirl::DuplicateDefinitionError on a sequence under Factory Girl v4.4.0.
It seems the sequences get handled differently within FG and simply wrapping all sequences in a rescue block will solve the issue.
For example:
begin
sequence :a_sequence do |n|
n
end
sequence :another_sequence do |n|
n*2
end
rescue FactoryGirl::DuplicateDefinitionError => e
warn "#{e.message}"
end
I have the same the problem. What I do is move the spec/factories.rb to spec/factories/role.rb
I renamed spec/factories as spec/setup_data and the problem gone.
Try renaming the spec/factories to anything that suites you, should work.
I had the same problem- make sure you aren't loading FactoryGirl a second time in your spec/support/env.rb file.
I had same problem. This happens becouse of you using gem 'refinerycms-testing'? wich requires factory-girl, so you should commit this gem, or commit gem 'factory_girl_rails', don't use all of this gems.
#gem 'refinerycms-testing', '~> 2.0.9', :group => :test
gem 'factory_girl_rails', :group => :test
or
#gem 'factory_girl_rails', :group => :test
gem 'refinerycms-testing', '~> 2.0.9', :group => :test
Please try following these steps
1) I looked for all occurrences of "factory_girl" from my RAILS_ROOT:
find . -name "*.rb" | xargs grep "factory_girl"
2) Because this was a full engine plugin "app" that I created via "rails plugin new --mountable", I had a file under RAILS_ROOT//lib/ called "engine.rb". It had:
config.generators do |g|
g.test_framework :rspec, :fixture => false
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
g.assets false
g.helper false
end
3) I also had the following in my spec_helper.rb file:
Dir["#{File.dirname(FILE)}/factories/*/.rb"].each { |f| require f }
4) the g.fixture_replacement line in engine.rb and the Dir line in spec_helper.rb were initializing the factories twice. I commented out the one from spec_helper.rb and that fixed the problem.
Alternatively, you can leave in spec_helper.rb and comment out in engine.rb.
Both fixed the problem in my case.
I had exactly the same problem.
It occurs when you use the scaffold generator.
It automatically creates a factory in test/factories/
So generally just deleting this file solve your issue
I had the same problem, it turned out there was a default users.rb created inside the test/factories which was created by the rails g command. This file was causing the conflict. The error went away when I deleted the file.
try to run
rake db:test:prepare
I just found I was getting this answer when accidentally calling cucumber features. When I just called cucumber, the problem went away.
I also ran with the same issue and commenting out a single line in spec_helper.rb file solved my problem.
Try commenting out this line from spec_helper.rb file and you should be good.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
I defined the same name factory at factories.rb, and I just found that someone else define the same factory below the directory of factories. So actually I can just use it without define another one.
Replace the refinerycms-testing gem with rspec-rails and factory_girl_rails
Check to see if you added factories through the model generator. My generator made a model and I added one to my main factory.rb file. Deleting the automatically generated ones worked for me.
In my case,
First my co-worker has setup the project with factory_girl gem with
Dir[Rails.root.join('spec/factories/**/*.rb')].each { |f| require f }
in rails_helper.
After some days, I replaced the gem with factory_girl_rails. Since this new gem also does that internally so factories were registered twice. This was causing the error.
Removed that line from rails_helper and it worked.
I solved this because I was trying to create two factories. My feature spec included the line:
let!(:user) { create(:user) }
And then I used a sign_up(user) helper method:
def sign_up(user)
visit '/users/sign_up'
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
fill_in 'Password confirmation', with: user.password_confirmation
click_button 'Sign up'
end
Back to my feature spec, I called:
context 'logging out' do
before do
sign_up(user)
end
...
thus effectively trying to sign up a User that was already being created by the factory.
I altered the sign_up(user) to sign_in(user), and the helper to:
def sign_in(user)
visit '/users/sign_in'
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
end
now the user argument creates the User in the db due to the let! block and the sign_up(user) logs them in.
Hope this helps someone!
oh! and I also had to comment out:
Dir[Rails.root.join('spec/factories/**/*.rb')].each { |f| require f }
as a lot of the other answers suggest.
The strangest thing, I got this error with the following syntax error in the code:
before_validation :generate_reference, :on: :create
:on: was causing this error. How or why will remain a mystery.
I resolved it by removing spec/factories/xxx.rb from command line:
rspec spec/factories/xxx.rb spec/model/xxx.rb # before
rspec spec/model/xxx.rb # after
for me, this issue was coming because was using both gems
gem 'factory_bot_rails'
gem 'factory_girl_rails'
to solve I removed gem 'factory_bot_rails' from gem file.
and also added require 'factory_girl' to spec/factories/track.rb file.
if Rails.env.test?
require 'factory_girl'
FactoryGirl.define do
factory :track do
id 1
name "nurburgring"
surface_type "snow"
time_zone "CET"
end
end
I hope this will help.
I solved this issue by just adding required: false to gem 'factory_bot_rails' like so:
gem 'factory_bot_rails', require: false
Check that you don't have multiple factories with same name this is one of reasons which causes error
Attempting to define multiple factories with the same name will raise an error.

How can I use FactoryBot in db/seeds?

Is it possible to do this?
If so, how can you do it?
Note: FactoryBot was previously named FactoryGirl
All you need to do is add require 'factory_bot_rails' to the db/seeds.rb file. This will give you access to your factories.
Note: Gem was previously called FactoryGirlRails
Josh Clayton, the maintainer of FactoryGirl, recommends against using FactoryGirl in your seeds file. He suggests using plain ActiveRecord instead.
(This answer works in rails 3.0.7)
I found the catch is how you set up the Gemfile - you need to do something along the lines of
gem 'factory_girl'
group :test do
gem 'factory_girl_rails'
end
We found problems having factory_girl_rails outside of the :test environment, which we didn't manage to get to the bottom of (maybe something to do with the way rails does class caching?)
Once that is done, I like to actually load data from a library in lib, something like...
require 'factory_girl'
require 'spec/factories/user_factory'
module Seeds
class SampleUsers
def self.run
u = Factory(:user)
end
end
And then running this method from within db:seed using
Seeds::SampleUsers.run
in db/seeds.rb
require 'factory_girl_rails'
10.times do
FactoryGirl.create :user
end
In Rails 5.2.6, you can create factories in your db/seeds.rb file. Add include FactoryBot::Syntax::Methods at the top of your seeds.rb file. Below that line, you can create your factories – i.e. user1 = create(:user).
# db/seeds.rb
include FactoryBot::Syntax::Methods
user1 = create(:user)
You can insert the following code into your spec_helper.rb, and it make some instances of the data you want (in this case "products" from the yaml file):
seeds_file = File.join(Rails.root, 'db', 'seeds.yml')
config = YAML::load_file(seeds_file)
config["products"].each do |product|
FactoryGirl.create(:product, product) if !Product.find_by_name(product['name'])
end

Resources