Using Rails model that is already declared - ruby-on-rails

Declaring a class for (fast) testing purposes is great:
require 'fast_helper'
require 'site_search'
class Post; end # This allows not to load the whole Rails env
describe SiteSearch do
it "searches on posts" do
Post.stub_chain(:scoped, :by_term).with("ruby").and_return ["post1", "post2"]
SiteSearch.by_term("ruby").should == ["post1", "post2"]
end
end
The problem with it is that it seems to break autoloading of rails models when the whole suite of specs is run.
The models aren't loaded anymore when the class gets declared before.
There are 4 ways of injecting the unloaded dependencies:
Declare the class (as in example here)
Set/remove const
Stub a wrapper method
Actually load them
I only want to use the 1st one.
The question: keeping the same specs structure, how can I tell rails to actually load the models even though the class is already declared?

In order for your empty class preemption trick to work, you must have set your app config.cache_classes = false, hence eager loading is not happening unless you call
Rails.application.eager_load!
When running the whole test suite you need to make sure classes preload, then empty redefinition should have no effect.
Now the question is how you control that it is run only if the full test suite is called. The honest answer is I don't know, but you can surely control it from the environment. Somewhere in your rspec helpers you initialize rails, update it to something like:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
Rails.application.eager_load! unless ENV["FORCE_EAGER_LOAD"].blank?
and then call rspec on full suite as:
FORCE_EAGER_LOAD=t rspec

Related

Intermittent ActiveRecord::RecordNotFound during Ruby on Rails Integration Tests

I have integration tests that are intermittently failing, and always with ActiveRecord::RecordNotFound errors. The error takes place inside the controller where a find call takes place given an ID from a fixture. It takes place in multiple controllers, though. I never see this behaviour while navigating the site, but I'd say the tests fail about 30-50% of the time. Running the test again after a failure seems to fix the problem.
If I load the fixtures manually into the development database, the ID that doesn't seem to be found is indeed present inside the tables.
I haven't been able to find much info on people having the same problem... any ideas?
UPDATE: Here are the contents of test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'capybara/rails'
class ActiveSupport::TestCase
# 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...
end
# Transactional fixtures do not work with Selenium tests, because Capybara
# uses a separate server thread, which the transactions would be hidden
# from. We hence use DatabaseCleaner to truncate our test database.
DatabaseCleaner.strategy = :truncation
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
# Make the Capybara Email DSL available in all integration tests
include Capybara::Email::DSL
# Stop ActiveRecord from wrapping tests in transactions
self.use_transactional_fixtures = false
# Switch to selenium as the default driver for JS support
Capybara.default_driver = :selenium
# Only click on visible links!
Capybara.ignore_hidden_elements = true
teardown do
DatabaseCleaner.clean # Truncate the database
Capybara.reset_sessions! # Forget the (simulated) browser state
Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver
end
end
UPDATE Here are the results of running the same test 5 times in a row. I ran the tests, waited until they finished, and then immediately ran them again with the command rails test:integration. NOTE: The E and F that are consistent throughout the tests are actually test errors -- I'm working on fixing those. For example, the second test run-through was "correct", but the first showed the spurious error.
..E......E..........F.
.........E..........F.
..E......E..........F.
..E......E..........F.
..E....E.E..........F.
The errors do occur across two separate tables -- they're not trying to find the same record. But it does appear to be only a subset of the tests that have this issue...
UPDATE Here's what the actual error looks like in the test results:
1) Error:
test_browsing_user_snops(BrowseStoriesTest):
ActiveRecord::RecordNotFound: Couldn't find User with id=980190962
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:341:in `find_one'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:312:in `find_with_ids'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/relation/finder_methods.rb:107:in `find'
/home/myuser/.rvm/gems/ruby-1.9.3-p286/gems/activerecord-3.2.9/lib/active_record/querying.rb:5:in `find'
/home/myuser/Projects/myproject/app/controllers/users_controller.rb:18:in `show'
...
Notice that the error appears when the controller tries to find a record. The relevant line is actually #user = User.find(params[:id]). It occurs with other models also, not just the users controller and not just the User model.
I'm concerned that there may be a delay during the truncation of data and the speed in which Capybara is driving the web browser which sometimes may result in identity column values starting at an unexpected value (i.e., 7 for record #1 - when you expect 1, because that identity generator has not yet been reset). I have no evidence that this is the case but its my best guess.
Take at look at item #3 at this URL. This is a pretty straight-forward hack, set use_transactional_fixtures to true and monkeypatch ActiveRecord by pasting that code into your test_helper.rb. This could help in eliminating any intermittent disk IO problems that could be a potential problem.
Another thing you can try is restricting your SQLite test database by setting the filename for that database to :memory: in your database.yml file. This should accomplish the same thing as above - eliminate spurious disk IO that may be causing these intermittent issues.

How can I use rspec with YAML fixtures that get cleaned up?

I want my rspec tests to load my YAML fixtures, and I want them to be loaded inside the transaction, so that they disappear after each test.
describe "the thing I'm testing"
fixtures :sandwiches, :condiments
before(:each) do
# stuff
end
it "makes sandwiches"
# making sandwiches
end
end
When the call to fixtures is there (in the describe block, but not in the before block), it loads my sandwiches and condiments, but outside of the transaction, and thus committed. Well, it looks that way; what's certain is that my fixtures are still in the db after the test finishes.
I thought perhaps I could put the call to fixtures in the before block, inside the transaction, but then I get
NoMethodError: undefined method `fixtures' for
<RSpec::Core::ExampleGroup::Nested_1:0x007fd1f11f7578>
So I guess I could use DatabaseCleaner, or something similar, but...can't I get what I want without that?
fixtures is a Rails method (not RSpec) that, afaik, doesn't actually load the fixtures - just registers them for loading later.
It is a class method, so it has to run in the class (the ExampleGroup returned by describe) and not the instance in which before, it, etc are evaluated.
Do you have config.use_transactional_fixtures = true in spec_helper.rb?

Rails isolated tests running under rails env

I'm currently working on a project and we started migrating our tests to isolated test (no rails dependency, and using stubs and mocks).
The thing is that until all the current tests are being isolated, we have to run the tests together with the isolated tests, which will start the rails environment.
The problem comes when, in the isolated tests, there is a fake class (class Foo; end;), it will override the original class for the rest of the tests.
Example:
In the foo_spec.rb we have this line
class Bar; end;
This would override the Bar class for the next nonisolated tests, and would cause a lot of fails.
There are 2 approaches I could figure in order to get rid of this:
- either comment out the fake classes when the tests are run with rails env
- put the isolated tests in another folder and run them separated from the rest (this would make more sense)
Can you think of a nicer way to deal with this?
we are using rspec (which should not change anything) and have our rails spec located in spec with their own spec_helper.rb file that is loading the env and all the ugly stuff.
in spec_fast folder we have all the spec that can run without rails, with their own spec-helper that only loads our independent lib folder.
for our ci-server we let both spec folder run in a different task:
if Rails.env.test?
require 'rspec/core/rake_task'
require 'ci/reporter/rake/rspec'
RSpec::Core::RakeTask.new(:all_fast) do |t|
t.pattern = 'spec_fast/**/*_spec.rb'
end
RSpec::Core::RakeTask.new(:all_slow) do |t|
t.pattern = 'spec/**/*_spec.rb'
end
task :all => ["ci:setup:rspec", :all_fast, :all_slow]
end
it should also be possible to just put them into separate subfolders like spec/rails and spec/fast but i did not try it out because it means to do a lot of path-changing in spec-files.
I don't know if it's right, but I end up not actually assigning such contextual dummy manually created dummy classes to constants.
Instead of:
#no
class Foo
#something
end
Instead:
foo = Class.new do
#stuff
end
And you can foo.new or foo.class_method to your heart's content. Could be in #foo too. But you aren't assigning it to the constant Foo like ordinary class definition does, you're creating an 'anonymous' class and assigning it to an ordinary variable, scoped to just within the area you need it.
Note: I'm not saying this is "right" way to do things with rspec, I never feel like I know the right thing to do, the right thing to do might be to somehow not create classes like this at all, or use some weird factorygirl thing which I don't understand or something. But when I need to create 'dummy' type classes just to the scope of a particular test or block, that's what I do.

Rails3: Unit testing a library doesn't load the fixtures

I added a unit test manually to test a library I'm building. I have some fixtures which are tested in their corresponding models without any issues.
The test case inherits from ActiveSupport::TestCase which runs fixtures :all.
require 'test_helper'
require 'mylib'
class MyLibTest < ActiveSupport::TestCase
#user = User.find(1)
test "Setup" do
assert_equal(#user.id, 1)
end
end
Even though user = User.find(1) works fine in every test that is actually for a model, this test raises an exception: /Library/Ruby/Gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/relation/finder_methods.rb:287:in 'find_one': Couldn't find User with ID=1 (ActiveRecord::RecordNotFound)
I have tried moving fixtures :all into the MyLibTest-class (suggested in another post) but with no difference.
How can I make my test work?
EDIT
I found out what was wrong, and it wasn't at all related to the fixtures. I wanted to use the same User object in several test cases so I added #user = User.find inside the class, but not inside a testcase. When I realized this and tried putting it in a test case, everything worked. Awkward :)
I can't be too sure exactly whats up without seeing how your Fixtures file is set up, but try these:
Try moving "fixtures :all" to test_helper.rb.
In the fixtures files, are you setting "id:" on each record? If not, the id is actually not going to be simply incremental - it is created based on the name of the fixture. Try using "User.first" instead of User.find(1). If you need the id to be 1, then set "id: 1" on the fixture.
Hope this helps.

Rails unit testing doesn't load fixtures

rake test:units fails in my current application, because the needed data of the fixtures is missing.
If I'm loading the fixtures manually via rake db:fixtures:load RAILS_ENV=test the unit tests are working, but rake purges the test database.
My test_helper includes fixtures :all and my tests are inheriting from it - but the fixtures are simply not loading.
I'm kind of clueless at the moment and could really need some help!
I've tried a lot and I think it has to do with some environment settings or plugins used in this project. Does anyone know where to read about which files are loaded for the testing environment?
Put the call to fixtures :all in your test class, not the super class (test_helper). My guess is that initialization of the super class isn't working the way you're expecting and that fixtures :all isn't be called. Perhaps try putting the call in the initialize method of test_helper.
My test/test_helper.rb looks like this:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
I finally found the problem, although the solutions is kind of hacky.
One plugin is relying that there is some data in the database, at least one row. So what happened was:
rake loads database schema
rake tries to load environment
environment includes plugin
plugin loading fails because of missing at least one row
no fixtures are loaded
The hacky solution is: put the needed data directly into schema and not into a fixtures, because it's loaded to late.
I'll search for a more convenient solution and will update this answer if I found one.
Another approach is to write your own custom rake task for testing.
For example:
task :test_units do
RAILS_ENV = 'test' # Force the environment to test
puts "Recreate the test database"
Rake::Task['db:test:prepare'].invoke
puts "Seed the database with fixtures"
Rake::Task['db:fixtures:load'].invoke
puts "Executing Unit Tests"
Rake::Task['test:units'].prerequisites.clear
Rake::Task['test:units'].invoke
end
My problem is forgot to put "require 'test_helper'" at the head.
eg.
require 'test_helper'
class AdminUserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
I had the same problem. Or rather, the problem was that my fixtures were not current with the database schema. Instead of throwing an exception, rails just used the test database as a fallback solution. Most iffy.

Resources