Forum: RSpec RSpec Testing ActiveRecord config dependency.

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
James B. (Guest)
on 2009-02-09 17:12
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,
David C. (Guest)
on 2009-02-09 20:31
(Received via mailing list)
On Mon, Feb 9, 2009 at 9:12 AM, James B. <removed_email_address@domain.invalid>
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
James B. (Guest)
on 2009-02-09 21:22
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. (Guest)
on 2009-02-10 00:23
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.
This topic is locked and can not be replied to.