RSpec Testing ActiveRecord config dependency

I have the following library code:

def normal_time_now
  return DateTime.now.utc if default_timezone == :utc
  return DateTime.now
end

This is dependent upon a setting in config/environment.rb

Make Active Record use UTC-base instead of local time

config.active_record.default_timezone = :utc

I want to test that I get the expected results with the config set to
utc and otherwise. The library code is used to automatically set a
model attribute on create. My existing specification looks somewhat
like this:

describe “Builds a Model with custom magic columns” do
before(:all) do
build_model :magiks do
string :description
# these are Rails’ own magic columns

# these are our custom magic columns

end
@my_mage = Magik.new
@my_mage.save!
end

it “should set each custom magic column present” do
(Magik.column_names & ActiveRecord::Base::HLL_AUDIT_COLUMNS).each do
|magic_column|
@my_mage.read_attribute(magic_column).should_not be_nil
end
end

I can simply create duplicate separate specification file and set the
configuration value appropriately in each. Something along the lines of:

describe “Builds a Model with custom magic columns” do
before(:all) do
config.active_record.default_timezone = nil
or
config.active_record.default_timezone = :utc

But this strikes me as inappropriate. I believe that this test belongs
inside the basic specification file but I cannot conceive of how to do
this. Basically, I require some form of variable setup routine. Does
anyone have any suggestions on how to handle this case?

Regards,

On Mon, Feb 9, 2009 at 9:12 AM, James B. [email protected]
wrote:

config.active_record.default_timezone = :utc

these are Rails’ own magic columns

|magic_column|
or
config.active_record.default_timezone = :utc

But this strikes me as inappropriate. I believe that this test belongs
inside the basic specification file but I cannot conceive of how to do
this. Basically, I require some form of variable setup routine. Does
anyone have any suggestions on how to handle this case?

I’d keep it isolated. You know that
config.active_record.default_timezone = :utc works, right? So you
don’t need to specify that. You’re specifying that when
default_timezone == :utc, it behaves one way, but otherwise it behaves
differently. So:

magik = Magik.create
def magic.default_timezone; :utc; end

OR

magik = Magik.create
magik.stub!(:default_timezone).and_return(:utc)

Now this violates a principle that you shouldn’t stub things on the
object you’re spec’ing, but the alternative is to play with global
values, which violates other principles. This is the simplest way IMO.
But if you’re concerned about internals of AR changing, you could do
this:

describe Thing do
before(:each) do
@original_default_timezone = Thing.default_timezone
end
it “does something” do
Thing.default_timezone = :abc
thing = Thing.new
thing.default_timezone.should == :abc
end
before(:each) do
Thing.default_timezone = @original_default_timezone
end
end

That will restore things after each example.

HTH,
David

David C. wrote:

Now this violates a principle that you shouldn’t stub things on the
object you’re spec’ing, but the alternative is to play with global
values, which violates other principles. This is the simplest way IMO.
But if you’re concerned about internals of AR changing, you could do
this:

Since my library extends ActiveRecord::Base and its magic datetime
columns I think that I should test that my extension does not change the
anticipated behaviour of AR itself.

However, default_timezone cannot be set on an instance of AR:Base
insofar as I can tell. Further, setting the default_timezone on AR:Base
itself does not appear to have any effect:

[ "local", "utc" ].each do |tz|
  ActiveRecord::Base.default_timezone = tz
  @my_mage = Magik.new
  @my_mage.description = tz
  @my_mage.my_time_local = DateTime.now
  @my_mage.my_time_utc = DateTime.now.utc
  @my_mage.save!
  puts @my_mage.description
end


it “should set the UTC time (for _at/_on) correctly” do
my_magic = Magik.find_by_description!(“local”)
puts " "
puts my_magic.description
puts “Local: #{my_magic.my_time_local} =
#{my_magic.my_time_local.to_f}”
puts " now: #{Time.now} = #{Time.now.to_f}"
puts " UTC: #{my_magic.my_time_utc} = #{my_magic.my_time_utc.to_f}"
puts " now: #{Time.now.utc} = #{Time.now.utc.to_f}"
my_magic = Magik.find_by_description!(“utc”)
puts " "
puts my_magic.description
puts “Local: #{my_magic.my_time_local} =
#{my_magic.my_time_local.to_f}”
puts " now: #{Time.now} = #{Time.now.to_f}"
puts " UTC: #{my_magic.my_time_utc} = #{my_magic.my_time_utc.to_f}"
puts " now: #{Time.now.utc} = #{Time.now.utc.to_f}"

Produces this:


local
Local: Mon Feb 09 14:20:06 UTC 2009 = 1234189206.0
now: Mon Feb 09 14:20:06 -0500 2009 = 1234207206.77585
UTC: Mon Feb 09 19:20:06 UTC 2009 = 1234207206.0
now: Mon Feb 09 19:20:06 UTC 2009 = 1234207206.77609

utc
Local: Mon Feb 09 14:20:06 UTC 2009 = 1234189206.0
now: Mon Feb 09 14:20:06 -0500 2009 = 1234207206.77807
UTC: Mon Feb 09 19:20:06 UTC 2009 = 1234207206.0
now: Mon Feb 09 19:20:06 UTC 2009 = 1234207206.7783
.

Finished in 0.429892 seconds

It seems that I am not overriding the AR:BASE.default_timezone setting
since, as far as I can tell, the values stored are the same whatever
value I set the default_tz to.

Does anyone have a technique to accomplish this?

James B. wrote:

It seems that I am not overriding the AR:BASE.default_timezone setting
since, as far as I can tell, the values stored are the same whatever
value I set the default_tz to.

Well, after a very frustrating afternoon I have finally uncovered what
is going on. The problem I had is that I did not consider that
AR::Base.default_timezone is used both in and out. The display of
datetime values retrieved via AR is adjusted according to the
default_timezone setting at the time of retrieval and is not based on
any TZ value stored in the database.

In my test setup I created rows with the tz value set to :utc and then
:local. However, when I ran the tests then I did not bother to reset
the tz appropriately for each test. Thus the display of the data was
adjusted by AR and confused the hell out of me.