Avoiding false-positive in tests: Don't use the same data
Let’s say that I have this class to test:
require "date"
class User
attr_accessor :created_at, :started_at
def initialize(created_at = nil, started_at = nil)
@created_at = created_at
@started_at = started_at
end
def created_at_or_started_at
@started_at || @created_at
end
end
And I wrote this test:
describe "User" do
context "when there is only created_at" do
subject do
User.new(Date.new(2018, 1, 1))
end
it "should return created_at" do
expect(subject.created_at_or_started_at).to eq Date.new(2018, 1, 1)
end
end
context "when there are created_at and started_at" do
subject do
User.new(Date.new(2018, 1, 1), Date.new(2018, 1, 1))
end
it "should return started_at" do
expect(subject.created_at_or_started_at).to eq Date.new(2018, 1, 1)
end
end
end
If I replace the method created_at_or_started_at with:
def created_at_or_started_at
@created_at
end
My tests will never break.
Test it by yourself, download this file and run:
$ rspec user_spec.rb
..
Finished in 0.00476 seconds (files took 0.16384 seconds to load)
2 examples, 0 failures
Now edit the file user_spec.rb that you just downloaded: change the second context to:
context "when there are created_at and started_at" do
subject do
User.new(Date.new(2018, 1, 1), Date.new(2018, 12, 31))
end
it "should return started_at" do
expect(subject.created_at_or_started_at).to eq Date.new(2018, 12, 31)
end
end
If you run again you will see it failing:
$ rspec user_spec.rb
.F
Failures:
1) User when there are created_at and started_at should return started_at
Failure/Error: expect(subject.created_at_or_started_at).to eq Date.new(2018, 12, 31)
expected: #<Date: 2018-12-31 ((2458484j,0s,0n),+0s,2299161j)>
got: #<Date: 2018-01-01 ((2458120j,0s,0n),+0s,2299161j)>
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-#<Date: 2018-12-31 ((2458484j,0s,0n),+0s,2299161j)>
+#<Date: 2018-01-01 ((2458120j,0s,0n),+0s,2299161j)>
# ./user_spec.rb:29:in `block (3 levels) in <top (required)>'
Finished in 0.02495 seconds (files took 0.16475 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./user_spec.rb:28 # User when there are created_at and started_at should return started_at
The point is here is to always use different data for different fields, this way you will always be sure of what field you are accessing.
#ProTip
When I write tests with Ruby on Rails I usually write code like:
User.new(
created_at: 2.days.ago,
started_at: 2.days.from_now,
)
It is easier to write and read then Date.new(YYYY, MM, DD).