I've got a Rails 4 beta app (on Ruby 2) and I'm getting an error I can't make sense out of.
I've got some specs that are failing because my model class has no method 'create', even though I'm inheriting from ActiveRecord::Base. The error message is calling my class a module (undefined method 'create' for Topic:Module), and that seems odd.
spec/models/topic_spec.rb:
require "spec_helper"
describe Topic do
it "should create a new topic given valid attributes" do
Topic.create!({:created_by_id => 1, :title => "Test" })
end
end
app/models/topic.rb
class Topic < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
validates :title => :presence => ture
validates :created_by_id => :presence => true
end
Error message:
$ rspec spec/models/topic_spec.rb
F
Failures:
1) Topic should create a new topic given valid attributes
Failure/Error: Topic.create!({:created_by_id => 1, :title => "Test" })
NoMethodError:
undefined method `create' for Topic:Module
# ./spec/models/topic_spec.rrc:15:in `block (2 levels) in <top (required)>'
It sounds like you have a module or namespace also named Topic that is getting loaded first and so in your tests, Topic is not referring to your class. Are there any other files that have Topic in them, even something like class Topic::Question or similar? If so, try taking them out or being explicit about it. For example, changing:
class Topic::Question < ActiveRecord::Base
to
class Topic
class Question < ActiveRecord::Base
Related
When running a test, if I try to create a new object using User.new I get an error. If instead I use User.new({}), it works fine.
Isn't params supposed to be defaulted to empty if not passed in?
$ rails -v
Rails 5.0.0.1
user.rb
class User
include ActiveModel::Model
attr_accessor :name, :email, :country
end
user_test.rb
require 'test_helper'
class User < ActiveSupport::TestCase
test "should create an empty user when initialized with no params" do
user = User.new
assert_not_nil(user, 'user cannot be empty')
end
end
test result
Error:
User#test_should_create_an_empty_user_when_initialized_with_no_parameters:
ArgumentError: wrong number of arguments (given 0, expected 1)
test/models/user_test.rb:7:in `new'
test/models/user_test.rb:7:in `block in <class:User>'
Generally, attr_accessor is used on a model for columns that are not actual columns in the SQL table.
So if your model has columns :name, :email and :country, then declaring attr_accessor is unnecessary. I think rails is waiting for you to declare them now.
Try commenting out the attr_accessor :name, :email, :country line, then re-run your test.
This may help
Instead of including model class extend it like:
class Crime < ApplicationRecord
//do your stuff here
end
Then you can use #user = User.new
The User you're referencing in the test is the test itself.
The easiest solution is to follow Ruby convention and name the test class UserTest.
Edit: This issue is resolved. The issue was caused by a conflict specific to my application. Another module dynamically created a method named .sources. I was able to troubleshoot by removing the relationship and inspecting the objects method list. Thanks anyways.
I'm using Rails 4.1 with Mongoid 4.0 and have setup a relation as follows:
class Organization
include Mongoid::Document
has_many :sources
end
and
class Source
include Mongoid::Document
belongs_to :organization
end
Then in my rspec test I have:
require 'rails_helper'
RSpec.describe PartnershipsController, :type => :controller do
describe "POST #record" do
it "should create a partnership for the source's organization" do
organization = FactoryGirl.create(:organization)
source = FactoryGirl.create(:source)
organization.sources.push source
end
end
There's more after, but the test fails at the organization.sources.push source line with:
undefined method `push' for nil:NilClass
I don't understand why the error is happening. Looks like in the mongoid documentation that's how I should be adding the related source, but so far no dice. What is the correct way to make this relation?
Edit: adding factory
Here's the organization factory, in case it helps clarify something:
FactoryGirl.define do
factory :organization do
app_name = Faker::App.name
company_name = Faker::Company.name
sequence(:name) { |n| "#{([app_name, company_name].sample)}#{n}" }
defaults_hash = { 'item_type' => 'charity', 'child_item_type' => 'product'}
defaults defaults_hash
end
end
I have an example of code not passing in test but working in the console.
Failing Test:
describe ImporterProfile do
it 'sends defaults method to EventAttribute model' do
expect(ListPage).to receive(:new) #passes
expect(EventAttribute).to receive(:new) #fails
ImporterProfile.new.standard_profile
end
1) ImporterProfile standard_profile sends new method to associated objects
Failure/Error: importer_profile.standard_profile
NoMethodError:
undefined method `each' for nil:NilClass
# ./app/models/importer_profile.rb:51:in `standard_profile'
# ./spec/models/importer_profile_spec.rb:29:in `block (3 levels) in <top (required)>'
Models:
class ImporterProfile < ActiveRecord::Base
has_one :list_page, dependent: :delete
has_many :event_attributes, dependent: :delete_all
accepts_nested_attributes_for :list_page
accepts_nested_attributes_for :event_attributes
def standard_profile
self.list_page = ListPage.new
self.event_attributes = EventAttribute.new
end
end
class EventAttribute < ActiveRecord::Base
belongs_to :importer_profile
end
class ListPage < ActiveRecord::Base
belongs_to :importer_profile
end
However, running this method in the console instantiates a new ImporterProfile, ListPage and several EventAttribute objects.
Anyone understand what is going on here?
I suspect that the problem is that you are mocking EventAttribute.new, but only returning nil, so Rails can't enumerate the active records as is required by the self.event_attributes = statement. (It needs to set the foreign key attribute of the EventAttribute records to the id of the ImporterProfile record.)
If you don't mind continuing with execution, you can do:
expect(EventAttribute).to receive(:new).and_call_original
Alternatively, you can return a double, but you'll need to provide stubs for whatever methods ActiveRecord requires, either by using a library such as http://rubygems.org/gems/rspec-active_record_mocks/versions/0.1.4 or by rolling your own.
As an aside, this question would have been a little easier to answer if you'd provided some way to associate the line numbers in the error stack trace with the sources you provided. Also, the comments on your expect statements that the first passes and the second fails is confusing because it appears that you are raising an error before the expectations are being checked.
I have next Siteable concern:
module Siteable
extend ActiveSupport::Concern
included do
belongs_to :site
scope :by_site, lambda { |site| where(site_id: site.try(:id)) }
scope :for_site, lambda { |site| by_site self.class.by_site(site).any? ? site : nil }
end
end
Example model with this concern:
class IndustryLink < LinkResource
require 'concerns/siteable.rb'
include Siteable
belongs_to :author, :class_name => 'User'
belongs_to :industry
validates_presence_of :name, :link
end
It work fine on the server. But all specs with this model fails with similar errors:
Failure/Error: industry = Factory(:industry, :name => 'new industry')
NoMethodError:
undefined method `by_site' for Class:Class
# ./app/models/concerns/siteable.rb:8:in `block (2 levels) in <module:Siteable>'
# ./app/models/industry_link.rb:12:in `get_objects'
# ./app/models/concerns/reorderable.rb:47:in `add_number'
# ./spec/views/sitemap/show.xml.builder_spec.rb:41:in `block (2 levels) in <top (required)>'
So, obviously that self.class is not Industry in this case and I don't know how to fix this.
If I move for_site to model and change self.class to Industry specs passes.
Checked for ruby 1.9.3, 2.1.1, Rails 3.2.19
Within your lambda self is already the model class - Industry, so the self.class is Class
You should just be using by_site (possibly unscoping it first, depending on what you need)
I have this custom validation method which makes sure you can't vote on your own content, and it causes quite a few RSpec test to fail with undefined method 'user_id' for nil:NilClass
Is there a way to rewrite the custom validation, or use a native rails validation?
failing tests
12) Vote votable type
Failure/Error: #vote = FactoryGirl.create(:vote)
NoMethodError:
undefined method `user_id' for nil:NilClass
# ./app/models/vote.rb:18:in `ensure_not_author'
# ./spec/models/vote_spec.rb:5:in `block (2 levels) in <top (required)>'
validate :ensure_not_author
vote.rb
attr_accessible :value, :votable_id, :votable_type
belongs_to :votable, polymorphic: true
belongs_to :user
def ensure_not_author
votable = self.votable_type.downcase
errors.add(:user_id, "You can't vote on your own content.") if self.votable.user_id == self.user_id
end
if anyone needs more code just shout
it looks like self.votable is nil. It appears as though this test should view the vote from the votable's point of view. If that's the case, then create the vote in the factory for your votable and move the test to your votable entity's suite, as you're really testing that the votable should not be able to vote on its own content.