how do I extend LOAD_PATH for MiniTest? - ruby-on-rails

[Aside: I am slowly answering my own question in "a concise recipe for installing, configuring and running minitest under autotest or Guard"]
My environment: Ruby 2.0. Padrino 0.10.7. Minitest 2.6.2. RackTest 0.6.2.
Short form: What is the best way to extend $LOAD_PATH to include my test directory so I can simply require 'test_helper' in my test files?
Long form:
Here's a sample test file. Note the require_relative "../../../test_helper" -- this requires keeping track of the test file relative to the test_helper.
# file: test/models/api/v0/index_test.rb
require_relative '../../../test_helper'
describe 'nobody home' do
it 'fetch fails' do
get "/api/v0/a_uri_that_does_not_exist"
last_response.status.must_equal 404
end
end
Here's the test helper:
# file: test/test_helper.rb
PADRINO_ENV = 'test' unless defined?(PADRINO_ENV)
require File.expand_path('../../config/boot', __FILE__)
class MiniTest::Unit::TestCase
include Rack::Test::Methods
def app
Demo.tap { |app| }
end
end
And finally, the rakefile that drives it (generated by padrino, invoked via padrino rake test):
# file: test/test.rake
require 'rake/testtask'
test_tasks = Dir['test/*/'].map { |d| File.basename(d) }
$stderr.puts("=== test_tasks = #{test_tasks}")
test_tasks.each do |folder|
Rake::TestTask.new("test:#{folder}") do |test|
test.pattern = "test/#{folder}/**/*_test.rb"
test.verbose = true
end
end
desc "Run application test suite"
task 'test' => test_tasks.map { |f| "test:#{f}" }
So: what would it take to replace the brittle require_relative '../../../test_helper' with a dependable and easily remembered require 'test_helper'?

you need to add libs:
Rake::TestTask.new("test:#{folder}") do |test|
test.pattern = "test/#{folder}/**/*_test.rb"
test.verbose = true
test.libs << 'test' # <-- this
end
Or if you invoke directly it with ruby:
$ ruby -Itest test/test_file.rb

Related

How to prevent RSpec from running specs twice in Rails plugin with dummy app?

I'm writing a Rails extension. To test it, I use RSpec. In order to make models to test the plugin on, I use a pregenerated dummy app:
rails plugin new yaffle --dummy-path=spec/dummy --skip-test --full
I have a few tests. When I call them, they run twice.
> # I have 4 tests in app total
> rspec
> 8 examples, 0 failures
> rspec spec/yaffle/active_record/acts_as_yaffle_spec.rb
> 4 examples, 0 failures
> rspec spec/yaffle/active_record/acts_as_yaffle_spec.rb:4
> 2 examples, 0 failures
This is how my files look like:
# lib/yaffle.rb
Gem.find_files('yaffle/**/*.rb').each { |path| require path }
module Yaffle
# Your code goes here...
end
# spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../dummy/config/environment", __FILE__)
RSpec.configure do |config|
config.example_status_persistence_file_path = '.rspec_status'
config.disable_monkey_patching!
end
# spec/dummy/config/environment.rb
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!
# spec/yaffle/active_record/acts_as_yaffle_spec.rb
require "spec_helper"
RSpec.describe "Acts as yaffle" do
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
assert_equal "last_squawk", Hickwall.yaffle_text_field
end
def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
assert_equal "last_tweet", Wickwall.yaffle_text_field
end
end
# spec/dummy/config/application.rb
require_relative 'boot'
require "rails/all"
Bundler.require(*Rails.groups)
require "yaffle"
module Dummy
class Application < Rails::Application
config.load_defaults 6.0
end
end
Also, I noticed that only files with require "spec_helper" are duplicated
So, what am I doing wrong? And, if it's a bug, how to work around it?
The cause
It was caused by this line:
Gem.find_files('yaffle/**/*.rb').each { |path| require path }
Apparently, Gem.find_files gets all files in gem directory that match that pattern - not only the files relative to gem root. So, yaffle/**/*.rb means both files in <GEM_ROOT>/lib/yaffle/... and <GEM_ROOT>/spec/lib/yaffle/....
https://apidock.com/ruby/v1_9_3_392/Gem/find_files/class
Fix
I fixed it by requiring all files explicitly:
require 'lib/yaffle/active_record/acts_as_yaffle'
require 'lib/yaffle/active_record/has_fleas'
It's also possible to just require all files from that directory:
Dir["lib/yaffle/active_record/**/*.rb"].each {|file| require file }

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

Minitest - NoMethodError: undefined method `get'

I was stuck with error when i run very simple test with minitest-rails gem.
I have rails 4.1.5 and minitest 5.4.0
rake test:controllers
1) Error:
DashboardController::index action#test_0001_anonymous:
NoMethodError: undefined method get' for #<#<Class:0x00000008e28170>:0x00000008eeb9b8>
test/controllers/dashboard_controller_test.rb:6:inblock (3 levels) in '
Test:
require "test_helper"
describe DashboardController do
context "index action" do
before do
get :index
end
it { must_respond_with :success }
it "must render index view" do
must_render_template :index
end
end
end
My test_helper:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require "minitest/rails/capybara"
class MiniTest::Spec
class << self
alias :context :describe
end
end
class RequestTest < MiniTest::Spec
include Rails.application.routes.url_helpers
register_spec_type(/request$/, self)
end
class ActionDispatch::IntegrationTest
# Register "request" tests to be handled by IntegrationTest
register_spec_type(/Request( ?Test)?\z/i, self)
end
class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
# Add more helper methods to be used by all tests here...
extend MiniTest::Spec::DSL
end
There are many things wrong with what you are doing. As I understand it you want to use Minitest's spec DSL in your Rails tests, correct? It looks like you are doing things to accomplish this that you don't need to do. I don't understand why half that code in your test_helper.rb file is there. I also suspect that you have other code doing things that are not being shown.
Here is what I did to reproduce your setup:
$ echo "Creating a new Rails app"
☣ [rails41:rails41] $ rails new undefined_get
☣ [rails41:rails41] $ cd undefined_get/
$ echo "Generate a Dashboard controller"
$ rails g controller dashboard index
$ echo "Add minitest-rails dependencies"
$ echo 'gem "minitest-rails"' >> Gemfile
$ echo 'gem "minitest-rails-capybara"' >> Gemfile
$ bundle install
$ echo "The test runs fine now:"
$ rake test
Run options: --seed 47210
# Running:
.
Finished in 0.457972s, 2.1835 runs/s, 2.1835 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
$ echo "Update to your test code and test_helper code"
$ echo "Use whatever editor you want. Not shown here."
$ echo "Now rerun the tests:"
$ rake test
rake aborted!
NoMethodError: undefined method `context' for #<Class:0x007f860258ae50>
The error I get is different than yours. You aliased the method context to describe in your test_helper.rb file, but unfortunately the object you aliased is not in the inheritance chain for the rails test objects. The rails test objects extend Minitest::Spec::DSL, but they do not inherit from Minitest::Spec. So, I am strongly suspicious that the code you provided is indeed producing the results you have presented. That said, here is the code in my test_helper.rb that will run your test:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/rails"
require "minitest/rails/capybara"
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Allow context to be used like describe
class << self
alias :context :describe
end
# Add more helper methods to be used by all tests here...
end
This is the standard test_helper.rb with two changes. First, it has the requires for minitest-rails and minitest-rails-capybara. That is all you need to do in order to enable the Minitest spec DSL in your rails tests. Second, it adds the alias for context to describe on ActiveSupport::TestCase, which is the basis for all the rails tests. If you want to add tests that do not inherit from ActiveSupport::TestCase then you can also alias it on Minitest::Spec, but that will not help you use context within your controller tests.
Still here? Okay. So why did your code give you a different error than mine? Likely the test object used for your controller tests isn't ActionController::TestCase. I say that because your error was undefined method get. The get method is something that ActionController::TestCase defines, and is not on Minitest::Spec. So, you somehow messed up your Minitest configuration. A simple way to make sure that your tests are using the correct test objects is to add an additional assertion to your test. Like this:
require "test_helper"
describe DashboardController do
context "index action" do
before do
# Make sure we are using the correct test class
self.class.ancestors.must_include ActionController::TestCase
# Continue with setup
get :index
end
it { must_respond_with :success }
it "must render index view" do
must_render_template :index
end
end
end
If that first assertion fails then you know you have done something wrong in your configuration.

testing rails engine generator with rspec

I create a simpe gem wich include a install generator, generator works fine but now I want to test it using rspec, I foud this gem, and try to test my generator, my spec code is:
require 'genspec'
require 'rosalie'
describe :install_generator do
it "should generate model" do
subject.should generate("message.rb")
end
end
rosalie is the name of may gem, now when I run it I got an error:
/stuff/work/my_projects/rosalie/lib/rosalie/engine.rb:2:in `': uninitialized constant Rosalie::Rails (NameError)
my engine.rb code is:
module Rosalie
class Engine < Rails::Engine
initializer "rosalie.models.messageable" do
ActiveSupport.on_load(:active_record) do
include Rosalie::Models::Messageable
end
end
end
end
anybody can help me with this problem?
You have to load your code before you include it somewhere.
Either require or autoload your main file.
Here is an example from my gem.
You need add these code in your spec_helper.rb, and require the spec_helper in each spec.
require File.expand_path("../dummy/config/environment", __FILE__)
require 'rspec/rails'

RSpec - shared DB conn. & transactional fixtures not working

I tried to follow José Valim's advice on faster test suites, but to no avail.
I use Spork and put the AR monkey patch in the Spork.each_run block (see the spec helper below).
However, my request specs fail because the database is not cleaned between runs - specifically, I get errors such as expected 1 record, got X when doing assertions like Model.should have(1).record.
Update
The problem lies with request specs using Javascript. See the following spec - it fails when I use js: true, but not if I remove that (I use RSpec's config.treat_symbols_as_metadata_keys_with_true_values = true, fwiw.)
# encoding: UTF-8
require 'spec_helper'
feature "Create a shift", :js do
scenario %q(
In order to plan the workload
As a coordinator
I want to schedule shifts
And I want to see when they're scheduled for
) do
visit shifts_path
click_link "new_shift_#{Date.current}"
fill_in 'shift_note', with: 'Casper - luk'
click_link_or_button 'submit'
Shift.should have(1).record
Shift.last.begins_at.should == Date.current
page.should have_selector("ol[data-date=\"#{Date.current}\"] li#shift_#{Shift.last.id}")
end
end
I can tell it's related to the DB not being cleaned, because it fails the first time (expected 1 record, got 0), passes the second time (because there's 1 record in the DB) and then fails again on any subsequent runs (expected 1 record, got 2 etc.)
I'm trying to avoid using a gem like DatabaseCleaner, to keep dependencies as few as possible, and to avoid a speed decrease in the test suite.
Any help/info/pointers are greatly appreciated!
Relevant info:
Rails 3.2.2
RSpec 2.9.0
Capybara 1.1.2
Capybara-webkit 0.11.0
FactoryGirl 2.6.4
Spork 0.9.0
Guard 1.0.1
Guard-spork 0.5.2
on a Macbook Air, OS X 10.7.3 (if that's relevant)
And my spec helper:
# encoding: UTF-8
require 'rubygems'
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.mock_with :rspec
config.use_transactional_fixtures = true
config.treat_symbols_as_metadata_keys_with_true_values = true
config.infer_base_class_for_anonymous_controllers = false
config.include Factory::Syntax::Methods
Capybara.javascript_driver = :webkit
Rails.logger.level = 4 # speed - http://blog.plataformatec.com.br/tag/capybara/
end
end
Spork.each_run do
require 'factory_girl_rails'
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
end
After much investigating, the problem seems to be with specs using JS in general, and not really the AR monkey patch.
I've re-phrased the problem in a new question here: RSpec+Capybara request specs w/ JS not working

Resources