I have a namespaced model, namely Billing::Plan. So I put its fixture under test/fixtures/test/billing/plan.yml. (Actually, it was the rails generator to put it there, so I assumed it was a convention over configuration pleasantness :-))
Now, when I run a single test it works, but when I try to run all my test suite with rake test or guard the fixture loading fails with this error
ActiveRecord::StatementInvalid: ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "plans" does not exist
The important part is
ERROR: relation "plans" does not exist
It seems like the fixtures :all command in my test_helper.rb file fails to understand that the relation name is billing_plans instead of plans.
Why is that?
I figured it out looking at what fixtures :all does.
It needs a call to set_fixture_class in order to correctly assign the table name to a namespaced fixture.
So I solved my problem adding...
# test/test_helper.rb
module ActiveSupport
class TestCase
fixtures :all
set_fixture_class 'billing/plan' => Billing::Plan # <= ...this line!
end
end
Related
I'm trying to test a named scope in my Rails model with RSpec using FactoryBot. I'm creating several records, where only one is returned by the scope.
RSpec.describe GemNamespace::GemModel, type: :model do
before(:all)
FactoryBot.create(:gem_model, :trait1) # id 1
FactoryBot.create(:gem_model, :trait2) # id 2
FactoryBot.create(:gem_model, :trait3) # id 3
end
let(:included_record) { GemNamespace::GemModel.find 1 }
describe 'my_named_scope' do
it 'returns only records matching the conditions' do
scope_results = GemNamespace::GemModel.my_named_scope
expect(scope_results).to contain_exactly(included_record)
end
end
end
The test is failing because even though included_record is the only record in the scope_results, some debugging shows that the included_record is actually a different Ruby object than the one in the results for some reason. Thus, the contain_exactly fails.
I've done scope testing like this on tons of models and it's always worked. The only difference with this one is that the model is defined inside a gem, and I'm extending its functionality by adding my named scope to it in my Rails app.
What am I missing? Why is it behaving like this only for this model?
If it matters:
Ruby 2.5.0
Rails 5.1.5
rspec 3.7.0
rspec-rails 3.7.2
factory_bot(_rails) 4.8.2
UPDATE: I'll put this here instead of editing the above. I am actually testing a database view as opposed to a table. The views do not have a unique id column, so I'm not actually doing a GemNamespace::GemModel.find 1 above, but instead a where(column: <condition value>).
I solved this with a workaround. I don't know too much about the internals of Rails, but it seems that the database view (and corresponding model) not having an id column kinda screws things up (i.e., the separate Ruby objects being created). So I simply compared all the values of the two objects "manually"
# As a workaround, we're just gonna convert them both to Ruby hashes using
# the #as_json method, and compare those instead.
expect(scope_results.as_json).to contain_exactly(included_record.as_json)
I created a plugin (gem) with my models that I'm using in two other apps.
I placed models to /lib folder and my app is working all good. But my question is where do I put future migrations? And how do I generate migrations inside the gem? Or do I have to write them manually?
To answer your first question the future migrations can be stored in /lib/db/migrate/ folder
Coming to the second part
Rails does not include any built-in support for calling migrations from plugins.But still you can run migrations by different methods
1.Create a Custom Rake Task
The first method is by using a rake task
Consider an example
namespace :db do
namespace :migrate do
description = "Migrate the database through scripts in vendor/plugins/pluginname/lib/db/migrate"
description << "and update db/schema.rb by invoking db:schema:dump."
description << "Target specific version with VERSION=x. Turn off output with VERBOSE=false."
desc description
task :pluginame => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate("vendor/plugins/pluginname/lib/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
end
end
2. Call Migrations directly
Dir.glob(File.join(File.dirname(__FILE__), "db", "migrate", "*")).each do |file|
require file
end
3. Migration Generator
These methods apart you can create your own migration generator.
Generating migrations has several advantages over other methods. Namely, you can allow other developers to more easily customize the migration. The flow looks like this:
call your script/generate script and pass in whatever options they
need
examine the generated migration, adding/removing columns or other
options as necessary
Providing the generator template example of railties gem
https://github.com/rails/rails/blob/master/railties/lib/rails/generators/migration.rb
Please Refer http://guides.rubyonrails.org/v2.3.11/plugins.html#migrations
for more details
Without minitest/spec, the test looks like this, and the my_engine_customers fixtures are loaded (all is well):
my_engine/test/models/my_engine/customer_test.rb is
require 'test_helper'
module MyEngine
class CustomerTest < ActiveSupport::TestCase
test "alex is id 5" do
assert my_engine_customers(:alex).id, 5
end
end
end
After adding require 'minitest/autorun' to test/test_helper.rb, and then
converting the above test :
require 'test_helper'
describe MyEngine::Customer do
let(:alex) { my_engine_customers(:alex) } # error here (error shown below)
it "alex is id 5" do
assert alex.id, 5
end
end
I get this error:
NoMethodError: undefined method `my_engine_customers' for
#<#<Class:0x007fb63e8f09e8>:0x007fb63e81b068>
How do I access fixtures when using minitest/spec ?
When you use the spec DSL you get a Minitest::Spec object to run your tests. But the Rails fixtures and database transaction are only available in ActiveSupport::TestCase, or test classes that inherit from it like ActionController::TestCase. So what you need is some way for the spec DSL to use ActionSupport::TestCase for you tests.
There are two steps to this, first ActiveSupport::TestCase needs to support the spec DSL. You can do this by adding the following code to you test_helper.rb file:
class ActiveSupport::TestCase
# Add spec DSL
extend Minitest::Spec::DSL
end
(Did you know ActiveSupport::TestCase.describe exists? You probably want to remove that method before you add the spec DSL if you plan on doing nested describes.)
Second, you need to tell the spec DSL to use ActiveSupport::TestCase. The spec DSL adds register_spec_type for just this purpose. So also add the following to your test_helper.rb file:
class ActiveSupport::TestCase
# Use AS::TestCase for the base class when describing a model
register_spec_type(self) do |desc|
desc < ActiveRecord::Base if desc.is_a?(Class)
end
end
This will look at the subject of the describe and if it is an ActiveRecord model it will use ActiveSupport::TestCase instead of Minitest::Spec to run the tests.
As you might expect, there are lots of other gotchas involved when you try to use the spec DSL for controllers and other types of tests. The easiest way IMO is to add a dependency on minitest-rails and then require "minitest/rails" in your test_helper.rb file. minitest-rails does all this configuration and more and makes the process much smoother. (Again, IMO.)
For more info see my blaurgh post Adding Minitest Spec in Rails 4. </shameless-self-promotion>
I have been working on a project for the last couple months, which was originally developed in 4.1.6 and I am now trying to update it to 4.2.0 (I have incrementally tested all version between 4.1.6 and 4.2.0, and all of the 4.1.x version work without error, and it is only when I go to 4.2.0 that I see the issue I describe here).
In this application, there is a lot of shared functionality that is common to ALL models, so I have created an abstract class, and all of my models inherit from that this class.
class TrackableModel < ActiveRecord::Base
self.abstract_class = true
...
end
class User < TrackableModel
...
end
The only change in the Gemfile was changing gem 'rails', '4.1.6' to gem 'rails', '4.2.0'
The update process was following the instructions HERE using rake rails:update and followed it up with the upgrade steps found in section 2 of the same document.
I overwrote all of the conflicting files with the defaults for this rake task, but reviewed each afterwards and worked in my modifications.
Before the update, all tests pass, but after updating
130 runs, 0 assertions, 0 failures, 130 errors, 0 skips
with the error
ActiveRecord::StatementInvalid: Mysql2::Error: Incorrect table name '': SHOW FULL FIELDS FROM ``
Error:
xxxTest#test_yyy:
NoMethodError: undefined method `each' for nil:NilClass
The application seems to work without any changes to the user experience. Everything looks good until I try to execute the tests.
I just can't get my tests to run.
Update:
I forgot to mention that I am running
ruby 2.1.5p273 (2014-11-13 revision 48405) [i386-mingw32].
Additionally, I have been working to follow the execution path. It seems to be failing while it is trying to setup the fixtures. It is going through a loop where it is building the schema_cache of tables. It queries the schema_migrations and my first custom table "customers" (during this call it iterates over each of the columns on this table which appears to be successful).
On the next call to
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.columns(table_name#String)
table_name's value is nil
Unfortunately I am still new enough to ruby/rails that I am having difficulties finding where the table_name values (schema_migrations, customers, nil, ...) is actually being set.
Can someone help point in in the direction of where the list of tables is coming from that is building the schema_cache?
I encountered the same issue and was able to resolve it. In my case, the base class that inherited from ActiveRecord included a belongs_to :foo statement. I moved that out to the sub-classes of the base class and the fixtures loaded.
So, here is an example:
class BaseAwesomeModel < ActiveRecord::Base
self.abstract_class = true
# This is what was causing the issue, moved to subclasses
# belongs_to :something
end
class AnotherModel < BaseAwesomeModel
belongs_to :something
end
class YetAnotherModel < BaseAwesomeModel
belongs_to :something
end
I'm not sure if this is what is causing your fixtures to fail to load, but it was the source of my issue.
Good luck!
Just a note for anyone else who comes across this issue and DOESN'T have a relationship defined in the base (abstract) class:
Scopes can also cause this issue with abstract classes (in the same way that belongs_to did for a previous answerer), but apparently if you move them to a class method it works:
Change:
scope :scope_name, -> { #code }
To:
def self.scope_name
#code
end
My problem was actually caused by something entirely different though. I was previously using the foreigner gem and it has definitions that can look like this in the migrations:
change_table :table_name do |t|
t.remove_foreign_key :column_name
end
If you move these to the new internalized rails syntax for foreign keys it works:
remove_foreign_key :table_name, :column_name
I would like to write some unit test with a logged user using Authlogic. To start right, I used some code hosted in http://github.com/binarylogic/authlogic_example. But I get an error after rake test, because of "test_helper.rb" and the following class:
class ActionController::TestCase
setup :activate_authlogic
end
Here is my error:
NameError: undefined local variable or
method `activate_authlogic' for
I think this Authlogic example is mapped over Rails 2; maybe it's a little bit different on Rails 3. Is there an other example where I can take example about unit test?
Many thanks.
Do you require 'authlogic/test_case' and include Authlogic::TestCase?
I had a similar issue (using rspec, though) and read through the code at http://github.com/trevmex/authlogic_rails3_example
As of Rails 3.1 and Authlogic 3.0.3, the only thing I've had to add to activate authlogic was
features/support/env.rb
Before do
activate_authlogic
end
I don't realize where to put include Authlogic::TestCase, so I put this after the requires in spec_helper.rb and it worked. There is a better place for it?