So I have a class TimeBank, and its being updated, and is getting two new virtual attributes which will manipulate two other existing attributes (DB columns).
class TimeBank < ActiveRecord::Base
# validations, consts, few other methods omitted for brevity
def date_worked
@date_worked ||= self.start.to_date
rescue
Date.current
end
def date_worked=(date_worked_val)
self.attributes['date_worked'] = date_worked_val
end
def hours_worked
@hours_worked ||= hours
rescue NoMethodError
nil
end
def hours_worked=(hours_worked_val)
self.attributes['hours_worked'] = hours_worked_val
end
def hours
if attributes["hours"]
read_attribute(:hours).to_f
else
((finish - start)/1.hour).to_f
end
rescue NoMethodError
0.0
end
# other methods omitted for brevity...
end
Everything works fine when using the application. When running tests, any time I instantiate a TimeBank, I only get the results of the rescue
statements above, namely, today's date for date_worked
and 0.0
for hours_worked
. The setters seem unresponsive, like they do nothing (while in tests).
I had also defined the setters to use write_attribute(:date_worked, 'SOMEVAL')
and while that works within the application, for tests, I get errors like
ActiveModel::MissingAttributeError: can't write unknown attribute hours_worked'
app/models/time_bank.rb:110:in `hours_worked='
An example of a test would be as follows:
describe "virtual attributes test" do
subject { TimeBank.new({member: @gus, admin_id: @addy.id, date_worked: Date.yesterday, hours_worked: 2.5, time_type: "store_shift", approved: true}) }
it "tests setter callbacks" do
subject.date_worked.must_equal Date.yesterday
subject.hours_worked.must_equal 2.5
end
end
Both assertions always fail, since they get today's date and 0.0 (the "defaults" as specified in the getters). If I set the attributes on the line immediately before the tests, it still has no effect.
Im not sure what Im doing wrong. Id like to write tests for these 2 virtual attributes, but any setting of their values seems to have no effect. Im not sure why it doesnt work within tests, but works fine within the application. The same code works as expected within the application controllers. Can anyone help me understand why?
The problem lies within the setter methods. Instead of assigning something to self.attributes["virtual_attribute"]
, an instance variable is needed to be set and returned, like so:
def virtual_attribute=(virtual_attr_value)
@virtual_attribute = virtual_attr_value
end
So for my example, I needed:
def hours_worked=(hours_worked_val)
@hours_worked = hours_worked_val
end
Both the application, and my getter/setter tests in minitest, behave as expected now.