10

I frequently find that I'd like to be able to use the test fixture finders (eg. users(:david)) from the Rails console. Is there a specific Rails module I can load at the console prompt, or a gem I can install, that would make these available?

My queries of the Google Oracle have not turned anything up, so I'm not holding my breath, but I hope somebody here on SO knows a secret and is willing to share.

Jon Garvin
  • 1,178
  • 9
  • 26
  • let me know if you want some factory_girl pointers. I am sure that it is powerful enough to manage whatever complexity you are currently handling in your fixtures. – Matt Dressel Feb 22 '13 at 23:19

5 Answers5

7

Figured it out myself. This works with both IRB and Pry. Just needed to add the following lines to my .irbrc (and/or .pryrc) file.

require 'active_record/fixtures'

def method_missing(method_name, *args, &block)
  if fixture_names.include?(method_name)
    method_name.to_s.singularize.titleize.constantize.find(fixture_id(args[0]))
  else
    super
  end
end

def fixture_id(label)
  ActiveRecord::FixtureSet.identify(label)
end

def fixture_names
  Dir["#{Rails.root}/test/fixtures/*.yml"].map { |filename| filename.match(/\/([^\/]+)\.yml/)[1].to_sym }
end

Now, assuming that there's a test/fixtures/users.yml file and it contains a fixture named diane:, then users(:diane) from the Rails console successfully returns that user.

m_x's answer to the original question was helpful in reaching this point.

Community
  • 1
  • 1
Jon Garvin
  • 1,178
  • 9
  • 26
  • Unfortunately this code can't be pasted into the console. To work around this, paste the fixture_names and fixture_id methods first (so method_missing doesn't recurse into infinity), then paste the rest. – bronson Aug 06 '14 at 05:32
  • And a small change to fixture_names would allow rspec-based fixtures to work too: Dir["#{Rails.root}/{test,spec}/fixtures/*.yml"] – bronson Aug 06 '14 at 05:43
  • FYI, Rails 4.2.1 deprecation warning. Use `ActiveRecord::FixtureSet` instead of `ActiveRecord::Fixtures`. – Justin C Jun 01 '15 at 18:46
2

I wish I could add to Jon Garvin's excellent answer. This is just like his version but doesn't use method_missing (which is kinda scary in the global context and doesn't allow tab completion). Also, it waits until you call load_fixtures from irb before loading your fixtures.

Insert this in your .irbrc or just paste from here.

# Loads all fixtures, then defines the Rails fixture helpers.
# For example: users(:jon) will load the jon fixture from fixtures/users.yml
def load_fixtures
  require 'active_record/fixtures'
  Dir["#{Rails.root}/{test,spec}"].each do |dir|
    Dir["#{dir}/fixtures/*.yml"].map { |filename| filename.match(/\/([^\/]+)\.yml/)[1].to_sym }.each do |name|
      ActiveRecord::FixtureSet.create_fixtures('spec/fixtures', name)
      define_method(name) { |*args|
        name.to_s.singularize.titleize.constantize.find(ActiveRecord::FixtureSet.identify(args[0]))
      }
    end
  end
end

Example usage:

$ irb
irb(main):001:0> load_fixtures
=> ["oweto/spec"]
irb(main):002:0> users(:chuy)
=> #<User id: 242462757, email: "chuy@example.com", password_digest: ...>
bronson
  • 5,612
  • 3
  • 31
  • 18
1

Interesting question.

Don't know if that helps, but the actual logic to load fixtures seems to sit in active_record/fixtures.rb - especially have a look at ActiveRecord::TestFixtures::ClassMethods#fixtures which is the macro you usually call inside a TestCase child class.

The logic seems to be well contained within the ActiveRecord::TestFixtures module, so maybe you could try to roll your own class that includes that module and require it in the console. (it seems you will need to instantiate this class before acessing fixtures though, as the methods generated by the module are instance methods)

m_x
  • 12,357
  • 7
  • 46
  • 60
  • Thanks. I explored the TextFixtures code and tried a few console experiments, but was unable to get anything to work. I'll keep experimenting when I have some free moments. – Jon Garvin Feb 15 '13 at 16:59
0

We use this to great effect with FactoryGirl.

If you are able to use factory_girl, it has some really nice features for mixing in traits and factory inheritance. With factory girl, accessing it via specs and accessing it via the console are very similar.

require 'factory_girl'
user = FactoryGirl.create(:user)

If you use bundler, you can create a gem group for your development and test environments that includes factory girl, then you will not need to require it when using the console in development.

group :development, :test do
  gem 'factory_girl'
end
Matt Dressel
  • 2,194
  • 16
  • 18
  • I've tried FactorGirl on several occasions in the past, and always ran into brick walls as soon as I needed to setup complex relationships between multiple objects. I can't remember any specifics other than a very sour factory flavored taste in my mouth. Never the less, in most cases, I would want to do this on mature projects with lots and lots of established and working fixtures, and trying to convert all that over to FactoryGirl just for this one benefit would probably not be a wise use of my time. – Jon Garvin Feb 15 '13 at 16:57
  • Although this is not exactly what you're looking for, you could follow the steps outlined here: http://stackoverflow.com/a/7905915/107840. This makes use of the rake db:fixtures:load task. – Matt Dressel Feb 18 '13 at 14:41
  • I looked into the Fixture source code and it doesn't look simple. The TestFixtures module does need to be included as m-x indicated. However, your instance needs to have setup/teardown callbacks and who knows what else. Once again, I really think you should use factory_girl or a different alternative to fixtures, if accessing fixture data in the console is a feature you really need. I can give you some tips to get you going with factory_girl if you're interested. – Matt Dressel Feb 18 '13 at 14:46
-1

The other answer is incomplete, you need to tell FactoryGirl to load the definitions :

require 'factory_girl'
FactoryGirl.find_definitions
user = FactoryGirl.create(:user)

See the docs for more details.

Ian Vaughan
  • 20,211
  • 13
  • 59
  • 79