Evitando falso-positivo em testes: Não use a mesma informação

#automated-test, #dev, #ruby

Digamos que eu tenha essa classe para testar:

require "date"

class Abc
  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

E que eu escrevi esse teste:

describe "Abc" do
  context "when there is only created_at" do
    subject do
     Abc.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
      Abc.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

Se eu substituir o método created_at_or_started_at com algo como:

def created_at_or_started_at
  @created_at
end

Meus testes nunca vão quebrar.

Teste você mesmo, baixe este arquivo e rode os tests:

$ rspec abc_spec.rb
..

Finished in 0.00476 seconds (files took 0.16384 seconds to load)
2 examples, 0 failures

Agora edite o arquivo abc_spec.rb que você acabou de baixar: mude o segundo context para:

context "when there are created_at and started_at" do
  subject do
    Abc.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

Se você rodar novamente, aí sim, o teste vai falhar: If you run again you will see if failing:

$ rspec abc_spec-rb
.F

Failures:

  1) Abc 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)>

     # ./abc_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 ./abc_spec-rb:28 # Abc when there are created_at and started_at should return started_at

#ProTip

Quando eu escrevo teste com Ruby on Rails eu normalmente escrevo algo como:

Abc.new(
  created_at: 2.days.ago,
  started_at: 2.days.from_now,
)

Veja que é muito mais fácil escrever 2.days.ago ou 2.days.from_now do que Date.new(YYYY, MM, DD).