Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I share the factories that I have in a GEM and use it in other project?

Tags:

I have a gem that includes some Factories. The gem looks something like:

.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── db
├── lib
│   ├── models
│   │   ├── users.rb
├── pkg
├── core.gemspec
├── spec
│   ├── factories
│   │   └── users.rb
│   ├── fixtures
│   ├── helpers
│   ├── integration
│   ├── spec_helper.rb
│   ├── support│   │ 
│   └── unit
│       └── users_spec.rb
└── tasks

Now i'm using the gem in another Ruby project (Grape) by adding something like gem 'core', git: 'https://url.git'.

Now everything is working fine as I can use User model from Grape project.

However I want to use the factories (users) so I can write further integration tests for Grape project.

In Grape project, in spec_helper.rb it looks like:

require 'rubygems'
require 'bundler/setup'
Bundler.require(:default, :development)

ENV['RACK_ENV'] ||= 'test'

require 'rack/test'

require File.expand_path('../../config/environment', __FILE__)

RSpec.configure do |config|
  config.mock_with :rspec
  config.expect_with :rspec
  config.raise_errors_for_deprecations!
  config.include FactoryGirl::Syntax::Methods
end

require 'capybara/rspec'
Capybara.configure do |config|
  config.app = Test::App.new
  config.server_port = 9293
end

Now my test 'users_spec.rb' looks like:

require 'spec_helper'

describe App::UsersController do
  include Rack::Test::Methods

  def app
    App::API
  end

  describe "/users/me" do
    context "with invalid access token" do
      before(:each) do
        get "/api/v2/users/me"
        user = build(:user)
      end      

      it 'returns 401 error code' do
        expect(last_response.status).to eq(401)
        expect(user).to eq(nil)
      end
    end    
  end
end

Now when I try to run the test using rspec spec/api/users_spec.rb I get :

I keep getting this error:

 Failure/Error: user = build(:user)
 ArgumentError:
   Factory not registered: user

Any help would be appreciated as I've been struggling for this.

like image 748
Eki Eqbal Avatar asked Jul 29 '15 12:07

Eki Eqbal


2 Answers

The problem is that you probably don't expose the spec folder (and herewith the factories) in the load path. Which, in general, is the right thing to do. Check you *.gemspec, you probably have something like:

s.require_paths = ["lib"]

This means only files under the lib directory can be required by other projects using your gem. See http://guides.rubygems.org/specification-reference/#require_paths=

So to solve your problem, you'd need to place a file inside the lib folder which 'knowns' where your factories are and requires those. So in you case, create a file lib/<your gem name>/factories.rb and add:

GEM_ROOT = File.dirname(File.dirname(File.dirname(__FILE__)))

Dir[File.join(GEM_ROOT, 'spec', 'factories', '*.rb')].each { |file| require(file) }

In the other Project load the factories with:

require '<your gem name>/factories'

Works fine for me. The only thing I havn't figured out yet is how to namespace your factories. Not sure if factory girl allows this.

like image 185
RngTng Avatar answered Oct 22 '22 03:10

RngTng


An alternative to require-ing each factory file as suggested in the other answer, is to update the FactoryBot.definition_file_paths configuration.

In your gem defining the factories:

Create a file which will resolve the factory path:

# lib/my_gem/test_support.rb

module MyGem
  module TestSupport
    FACTORY_PATH = File.expand_path("../../spec/factories", __dir__)
  end
end

In you app / gem using the factories from the other gem:

# spec/spec_helper.rb or similar

require "my_gem/test_support"

FactoryBot.definition_file_paths = [
  MyGem::TestSupport::FACTORY_PATH,
  # Any other paths you want to add e.g.
  # Rails.root.join("spec", "factories")
]

FactoryBot.find_definitions

The advantage of the definition_file_paths solution is that other functionality like FactoryBot.reload will work as intended.

like image 42
odlp Avatar answered Oct 22 '22 04:10

odlp