Today I was testing a small helper method I had written that would convert a String to a date or if it was already a DateTime object it would just return it. A pretty simple function but one that needs testing all the same. Improving test suites is one of the services we offer
Initially my test looked something like this;
require 'spec_helper'
require 'date'
require 'time'
describe MyHelper do
describe 'convert_to_datetime functionality' do
let(:input) { '2012-03-04' }
let(:date_version) { DateTime.parse(input) }
it 'should successfully convert a well formatted date string' do
output = convert_to_datetime input
output.should == date_version
end
it 'should return the original date if a DateTime object was passed in' do
output = convert_to_datetime date_version
output.should == date_version
end
end
end
On the face of it this would seem to work, and indeed all the tests pass, but what if I changed output.should == date_version to output.should == input? Instant fail? Nope!
The reason why this also passes is because I am including ActiveSupport earlier on in the code and with that included so long as the String when converted to a DateTime is the same DateTime then .should == will pass.
This was unexpected, but fortunately I very quickly found be_an_instance_of, what this allows us to do is check that a variable is an instance of a particular class, in our example a DateTime class.
Now my code reads;
require 'spec_helper'
require 'date'
require 'time'
describe MyHelper do
describe 'convert_to_datetime functionality' do
let(:input) { '2012-03-04' }
let(:date_version) { DateTime.parse(input) }
it 'should successfully convert a well formatted date string' do
output = convert_to_datetime input
output.should be_an_instance_of(DateTime)
output.should == date_version
end
it 'should return the original date if a DateTime object was passed in' do
output = convert_to_datetime date_version
output.should be_an_instance_of(DateTime)
output.should == date_version
end
end
end
Which is a more solid check.
Update
Some nice folk on Reddit pointed out that I could have done one of the following;
- Swapped
output.shouldfordate_version.shouldwhich would force rspec not to change my DateTime into a String. - Used
eqlinstead of==which does not do type conversion. - Using be_instance_of can make your code a little harder to read and might end up coupling your test to a particular data type and if that changes you could end up with stuff to fix.
Rspec an_instance_of
The an_instance_of method in Rspec is an alias for instance_of, and this
method will return true if the class is the same between the thing being tested
and what you are testing against.