1

I have couple hundred tests and I work with date/time a lot. In some tests, I aim for output format, elsewhere, I check date ranges. Therefore, my tests have lots of these:

FactoryGirl.create(:foo, ended_at: Time.zone.local(2014, 5, 5, 22, 15))

I thought, maybe there's a shortcut to these, since in some test files, I only work with few timestamps.

FactoryGirl.create(:foo, ended_at: may_5_22_15)

Where may_5_22_15 would be defined in method or using let.

Is this a good way to go?

Update: More context (thanks @Snowman for pointing out).

Motivation for this is to improve readability of testsuite. It makes it easier to me to read "Foo ended at May 5th by 22:15" rather than parsing a bunch of numbers in Time constructor.

The model has bunch of date/time attributes (ended_at, started_at, closed_at...) and the more specs I write, the more Time constructors I use and re-use. So I thought maybe I could do something like this:

let(:may_5_22_15) { Time.zone.local(2014, 5, 5, 22, 15) }

What I'd like to know is whether this is a good practice or not, in terms of test suite maintenance, readability or if there are any reasons why not to do it.

rdamborsky
  • 111
  • 3

3 Answers3

1

Named constants are often a better idea than magic numbers. In this context, however, there's not a lot of magic involved. On the other hand, in some languages, the time-value constructor uses a zero-ordinal value for only some of its values (e.g., JavaScript's Date()'s month number), or have illogical behavior such as accepting out-of-range values as non-integral multiples of the next-higher order (again, JavaScript's Date()). In such environments, a named constant for a timestamp makes excellent sense.

Ross Patterson
  • 10,277
  • 34
  • 43
0

I am not sure where the ended_at method is defined, but there is no reason why you could not extend it so that it would take whatever kind of string input you want using Time.parse

Dmitri
  • 1,030
  • 6
  • 18
0

Just create a specific factory or use default with redefinable value ended_at, as follows:

factory :foo do
   ended_at { |foo| foo.ended_at || Time.zone.local(2014, 5, 5, 22, 15) }
end

or with ignore block:

factory :foo do
   ignore do
      ended_at { Time.zone.local(2014, 5, 5, 22, 15) }
   end

   after :build do |foo, evaluator|
      foo.ended_at = evaluator.ended_at
   end
end

or pass some usual symbol with encoded datetime in its name:

factory :foo do
   ignore do
      ended_at { '' }
   end

   after :build do |foo, evaluator|
      foo.ended_at = Time.strptime(evaluator.ended_at, '%b_%e_%k_%M')
   end
end