ruby-on-railsruby-on-rails-4testingminitestvirtual-attribute

Rails 4 Minitest Testing Virtual Attributes


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?


Solution

  • 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.