سؤال

I found a blog post about Testing Factories First (by BigBinary - which happens to be a Minitest/spec version of Thoughtbot's RSpec original).

Could you please show me the equivalent without the spec framework - just with Minitest (Rails)?

The Thoughtbot approach (RSpec)

spec/factories_spec.rb

FactoryGirl.factories.map(&:name).each do |factory_name|
  describe "The #{factory_name} factory" do
     it 'is valid' do
      build(factory_name).should be_valid
     end
  end
end

Rakefile

if defined?(RSpec)
  desc 'Run factory specs.'
  RSpec::Core::RakeTask.new(:factory_specs) do |t|
    t.pattern = './spec/factories_spec.rb'
  end
end

task spec: :factory_specs

The BigBinary approach (Minitest, spec)

spec/factories_spec.rb

require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe FactoryGirl do
  EXCEPTIONS = %w(base_address base_batch bad_shipping_address)
  FactoryGirl.factories.each do |factory|
    next if EXCEPTIONS.include?(factory.name.to_s)
    describe "The #{factory.name} factory" do

      it 'is valid' do
        instance = build(factory.name)
        instance.must_be :valid?
      end
    end
  end
end

lib/tasks/factory.rake

desc 'Run factory specs.'
Rake::TestTask.new(:factory_specs) do |t|
  t.pattern = './spec/factories_spec.rb'
end

task test: :factory_specs

What is the Minitest equivalent (without spec)?

هل كانت مفيدة؟

المحلول

The approach I am presenting below is slightly different than the two original solutions - in the sense that my approach creates only one test, within which I cycle through the factories and run an assertion against each. I was not able to create a solution that mimics the original solutions any closer - which is (I believe) a separate test method for each factory. If someone could show such an implementation, that would be cool.

test/aaa_factories_tests/factories_test.rb

require File.expand_path(File.dirname(__FILE__) + '/../test_helper.rb')

class FactoriesTest < Minitest::Unit::TestCase

  puts "\n*** Factories Test ***\n\n"

  EXCEPTIONS = %w(name_of_a_factory_to_skip another_one_to_skip)

  def test_factories

    FactoryGirl.factories.each do |factory|

      next if EXCEPTIONS.include?(factory.name.to_s)

      instance = FactoryGirl.build(factory.name)
      assert instance.valid?, "invalid factory: #{factory.name}, error messages: #{instance.errors.messages.inspect}"

      instance = factory = nil
    end         
  end   
end

Thanks to the way Minitest works out of the box -- add any directories under test/ and minitest-rails will automatically create the associated rake task for it. So let's say you add a test/api/ directory, rake minitest:api will automagically be available. -- I see the task when I run bundle exec rake -T with no other configurations:

rake minitest:aaa_factories_tests    # Runs tests under test/aaa_factories_tests

And I am able to run this task successfully:

-bash> bundle exec rake minitest:aaa_factories_tests

*** Factories Test ***

Run options: --seed 19208

# Running tests:

.

Finished tests in 0.312244s, 3.2026 tests/s, 9.6079 assertions/s.

1 tests, 3 assertions, 0 failures, 0 errors, 0 skips

Despite the ugliness of prepending the directory with aaa, I am able to have the factories tested first with:

bundle exec rake minitest:all

The reason for the aaa prepend solution is MiniTest does a Dir glob and on Mac OS X (and other Unix variants) the results are sorted alphabetically (though the results differ across different platforms).

As well, I prepended the default_tasks array with aaa_factories_tests to have the factories tested first in the default Minitest task (i.e. when running bundle exec rake minitest).

lib/tasks/factories_first.rake

MiniTest::Rails::Testing.default_tasks.unshift('aaa_factories_tests') if Rails.env =~ /^(development|test)\z/

Note that the above condition avoids erroneously referencing Minitest in environments where it is unavailable (I have confined minitest-rails to :test and :development groups in Gemfile). Without this if-condition, pushing to Heroku (for example to staging or production) will result in uninitialized constant MiniTest.

Of course I am also able to run the factories test directly:

bundle exec ruby -I test test/aaa_factories_tests/factories_test.rb

نصائح أخرى

Here is a solution for MiniTest without the spec framework:

test/factories_test.rb

require File.expand_path(File.dirname(__FILE__) + '/test_helper')

class FactoriesTest < ActiveSupport::TestCase
  EXCEPTIONS = %w(griddler_email)
  FactoryBot.factories.map(&:name).each do |factory_name|
    next if factory_name.to_s.in?(EXCEPTIONS)
    context "The  #{factory_name} factory" do
      should 'be valid' do
        factory = build(factory_name)
        assert_equal true, factory.valid?, factory.errors.full_messages
      end
    end
  end
end

lib/tasks/factory.rake

namespace :test do
  desc 'Test factories'
  Rake::TestTask.new(:factories) do |t|
    t.pattern = './test/factories_test.rb'
  end
end

task minitest: 'test:factories'

The most important thing is to use taks minitest instead of task test if you want the factories tests to be run before other tests.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top