I'm just getting into testing and I have many models that utilize a has_many though relationship. In each case, one model requires that the other be present at the time of save. I've run into a wall with every testing system I've tried (FactoryGirl, Fixtures, and now Fabrication) where I can not figure out how to set up the tests correctly to replicate this behavior.
I followed this GIST as an example but changed the after_build to before_save as the models are requiring the "though" model at that time. Am I approaching this the wrong way? How does one test this relationship/functionality?
I've created a GIST that is hopefully easier to use/read.
I changed this
Fabricator(:brand) do
title "Coca Cola"
before_save do |brand|
styles Fabricate(:brand_style, :brand => brand, :style => Fabricate(:style))
end
end
to this
Fabricator(:brand) do
title "Coca Cola"
styles(count: 3) { Fabricate(:style) }
end
and now the test is passing. However, I'm not sure if this is the correct way of setting this up, so if anyone has any additional insight it would be appreciated.
Related
I have two interdependent models, account and user. An account is always created by a user, whose id is thus stored in the account's creator_id attribute, and a user necessarily belongs to an account (but there's no limit on the number of users belonging to an account), this information being stored in user's account_id attribute. The same user can have created different accounts.
I expressed these rules this way :
User model :
belongs_to :account, inverse_of: :users
has_many :created_accounts, class_name: "Account", :foreign_key => "creator_id"
Account model :
belongs_to :creator, class_name: 'User', optional: true
has_many :users, inverse_of: :account
As they are interdependent, I used a dirty workaround to be able to instantiate them : I create first the account, and following that action I force the user to create their profile, and their user_id is added to the account as creator_id in an update.
That's why I have, in Account model :
validate :require_actual_creator_id, on: :update
------------------------------------------------
def require_actual_creator_id
User.find(creator_id)
end
I was working on an authentication system only involving the user model, so I had these lines commented out until yesterday when I uncommented them.
I ran db:migrate:reset, db:seed and db:migrate RAILS_ENV=test without any problem, both models have a normal behavior in the console, but when it comes to fixtures (e.g. testing or db:fixtures:load), I got the following error :
NoMethodError: undefined method `id' for nil:NilClass
/home/vincent/workspace/bam-rails/test/fixtures/users.yml:16:in `get_binding'
Here is one typical fixture causing the problem, the line 16 being the commented one :
michael:
id: 1
handle: Michael Example
email: michael#example.com
encrypted_password: <%= User.generate_encrypted_token('password') %>
role_id: <%= User::ADMIN %>
is_activated: true
activated_at: <%= DateTime.now %>
#account_id: <%#= Account.first.id %>
When I comment this last line, there's no problem anymore. However, I'd like to load proper fixtures for my tests, because for example the user created here is not valid.
I read in this post that the fixtures load in the alphabetical order. If this is right, I can't get why I have to comment this line, because accounts is supposed to be loaded before users.
I found that solution to work but it's not from the official documentation and it's quite old, dated back to 2007. I am afraid this would stop working from one day to the next.
Does anyone know how to properly load the fixtures in a custom order in Rails 5, or has another solution to my problem ?
Thank you in advance.
The problem you have is entirely stemming from how you organized your code. That work around where you create the first account is where you are having an issue. So the fact that by the time your user is instantiated your account does not exist first because the fixtures are not loaded yet; of this I am sure you are aware. Fixtures are notoriously brittle this is why people often move away from them the more complex their code gets. In this case though the are helping you expose a code smell, anytime the order or running you test or basic non test case specific set up causes issues that means you have an issue with your code. I would suggest you find a way around using this "dirty work around".
Now if for some reason you are married to the way your code is currently organized I suggest maybe switching to factory girl, it will give you a little more flexibility to control the point at which your mock objects are instantiated that way you wont run into this issue. I will however say this will just enable you to continue you down this path that will more than likely just lead to more issues down the road, your best bet is to reimplement the feature.
We're building an intranet in Ruby on Rails and now we want to add functionality for having reminders when you should have done something but haven't.
For example, say you have a model, Meeting, I want to send reminders to everyone who has had a meeting but where the meeting report is empty (meeting.date < Date.today && meeting.report == ""). And a project model where you have some other criteria (maybe project.last_report.date < lastMonday && !project.closed)
Now, I can imagine creating these reminders with some kind of rake task and then removing them by some event trigger when you update Meeting or whatever, but that seems like I'll get spaghetti code everywhere.
My second idea is to make a separate module that, on each page load, fetches all the entries that could be related and runs all these checks, and then returns Reminders, however, this will probably be hella slow to hit the database like this. (Maybe caching would be an option but still)
So, anybody done something like this and have any ideas on how to solve our problem?
Thanks!
I can't see any issue with spaghetti code if you let each object that requires a Reminder to manage it's own reminders. If you want to be an OOP purist you could probably create a separate class (e.g., MeetingReminderManager in your example) to manage the reminders but that seems like overkill here. Consider...
class Reminder
belongs_to :source, polymorphic: true
belongs_to :user
end
class Meeting
has_many :reminders, as: :source
has_many :users
after_create :build_reminders, if: ->{|meeting| meeting.report.blank? }
after_update :destroy_reminders, if: ->{|meeting| !meeting.report.blank? }
private
def build_reminders
users.each{|user| self.reminders.create user_id: user.id, due_on: self.date }
end
def destroy_reminders
self.reminders.delete_all
end
end
I don't see a problem with spaghetti and background job in ruby on rails. I think making them is the path to go. Check whatever is suit you: http://railscasts.com/?tag_id=32
I'm trying to get associations in FactoryGirl to work, and they just ... don't. I've basically got this:
class Foo
include Mongoid::Document
belongs_to :bar
end
class Bar
include Mongoid::Document
has_many :foos
end
FactoryGirl.define do
factory :foo, class => Foo do
bar
end
factory :bar, class => Bar do
end
end
At least so the docs lead me to believe... But then in my test, I have
a_foo=FactoryGirl.create :foo
a_foo.bar # Hooray! It's an associated object
Foo.where( _id: a_foo._id ).includes( :bar ).first.bar # This is nil!
Why is the associated value nil on the last line? I need it not to be, because the actual code being tested does this same thing, and it has a right to expect it to work... What am I missing about why this doesn't work right? Something to do with eager loading, perhaps?
Your code actually works for me with FactoryGirl 4.2.0, Mongoid 3.0.9. But I've run into similar issues when I've been running mongoid with the identitymap disabled (which is default behavior). Without the identitymap, you can have two different ruby objects representing the same document in the database, getting out of sync with each other. So, if you have autosave off, for example, this could cause the problem you're seeing.
Try pasting your simplified code into the rails console yourself -- if it works, then you probably changed something significant in pairing down your real code. (Sorry to point out the obvious, but the fact that you have a syntax error in your factory code makes me think you didn't actually test your sample code.)
I have the following model:
class Kueue < ActiveRecord::Base
attr_accessible :name, :user_id
belongs_to :user
has_and_belongs_to_many :photos
scope :empty, includes(:photos).where{ photos.id.eq(nil) }
scope :with_photos, includes(:photos).where{ photos.id.not_eq(nil) }
end
I want to write specs for the scopes, to make sure they're reliably working. My problem is how to deal with the Photo model. Photos have a lot of validations on them, for instance they must belong_to a User. So, I can write a working spec for these queries like so:
describe "queries" do
before :each do
#empty = Fabricate(:kueue)
#full = Kueue.new :name => "Full Queue"
#full.photos << Fabricate(:photo)
#full.save
end
it "should find empty queues" do
Kueue.empty.should_not include(#full)
end
it "should find queues with photos" do
Kueue.with_photos.should_not include(#empty)
end
end
However, as you can imagine this is sloooow. This makes a bunch of calls to the database. (1) to make two kueues, (2) to make a photo, (3) to make a user who owns the photo... and so on, down the line.
This seems like a simple enough problem, all I need is one join record between a photo and a kueue and I can test this really fast. So how would you go about mocking this interaction so you can test it faster and in better isolation?
PS: I'm using Rails 3.2.8, Squeel (hence the query syntax) and my model is called Kueue because Queue is a reserved word in Rails :)
i think that it would not make sense to mock in the context of scopes.
scopes are basically database-queries and you want to make sure they work with, you know, the database.
so if your problem is test-performance, then i would suggest:
try out fixtures in this case
OR
skip model validation, to reduce the models needed
I'm trying to create a nested child and grandchild record. The child belongs_to both the parent and the grandchild. The child won't validates_presence_of the grandchild because it hasn't been saved yet.
I'm using Rails 2.3.11, Formtastic, InheritedResources, and Haml, and everything else seems to work correctly - for example, validation errors on the grandchild populate properly in the parent form, and the invalid values are remembered and presented to the user. The parent model doesn't even try to update unless everything is valid, just as it should be.
My code is something like this, though in a different problem domain:
class Project < ActiveRecord::Base
has_many :meetings, :dependent => :destroy
accepts_nested_attributes_for :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :project
belongs_to :task
accepts_nested_attributes_for :task
validates_presence_of :task_id, :project_id
end
class Task < ActiveRecord::Base
has_many :meetings, :dependent => :destroy
end
The Project ALWAYS exists already, and may already have Meetings that we don't want to see. Tasks may belong to other Projects through other Meetings, but in this case, the Task and Meeting are ALWAYS new.
In the controller, I build a blank record only on the new action
#project.meetings.build
and save the data like this:
#project.update_attributes(params[:project])
and in the view
- semantic_form_for #project do |f|
- f.semantic_fields_for :meetings do |m|
- next unless m.object.new_record?
= m.semantic_errors :task_id
- m.object.build_task unless i.object.task
- m.semantic_fields_for :task do |t|
- f.inputs do
= t.input :task_field
= m.input :meeting_field
When I try to save the form, I get a validation error of "Task can't be blank." Well, sure, the Task hasn't been saved yet, I'm trying to validate, and I don't have an ID for it.
Is there a simple and elegant way to make sure that the grandchild record (Task) gets built before the child record?
I've tried something like this in the Meeting model:
before_validation_on_create do |meeting|
meeting.task.save if meeting.task.valid?
end
and that seems to save the Task, but the Meeting still doesn't get the right ID. Same error, but the Task record gets created.
I've also tried this:
before_validation_on_create do |meeting|
new_task = meeting.task.save if meeting.task.valid?
meeting.task = new_task
end
Which has the strange behaviour of raising ActiveRecord::RecordNotFound "Couldn't find Task with ID=XX for Meeting with ID=" - which I sort of get, but seems like a red herring.
I also tried adding :inverse_of to all the relationships and validating :task instead of :task_id. The latter, oddly, fails but seems to give no error message.
My actual goal here is to create more than one Task, each with an initial Meeting on a previously selected Project... so I could take another approach with my problem - I could do something simple and ugly in the controller, or create the first Meeting in an after_create on the Project. But this is so pretty and soooo close to working. The fact that I'm getting proper validation errors on :task_field and :meeting_field implies that I'm on the right track.
I see what the problem is, but not how to solve it: I suspect I'm missing something obvious.
Thank you!
Well, I found a solution, based on one of the similar questions out there, but the short of it is "rails 2.3 doesn't seem to be very good at this." I think I can put the answer in a more succinct way than any of the other answers I've seen.
What you do is you skip the validation of the :task_id, but only if task is valid! Most of the other answers I've seen use a proc, but I think it's more readable using delegate, like this:
delegate :valid?, :to => :task, :prefix => true, :allow_nil => true
validates_presence_of :task_id, :unless => :task_valid?
I also had another problem hidden under the waterline - in the case, the "Project" is actually a special sort of record that I wanted to protect, which has a validation that (intentionally) fails only for this special record, and I also set readonly? to true for the special record.
Even though I'm not actually changing that special record, it still needs to validate and can't be readonly to update children through it. For some reason, I wasn't seeing the error message for that validation. To solve that, I made the validation on the Project only applicable :on => :create, and I took out the readonly? thing.
But the general solution is "don't validate presence of the unbuilt belongs_to object if the object itself is valid." Nil is never valid, therefore the validation still works if you just have an object_id.
(Please don't vote down a sincere question unless you have an answer or a link to one. I'm aware the question has been asked by others in other ways, I read many of those other questions, none seemed to be precisely the same problem, and I had not found a solution.)