Marcio Trindade

A nova sintaxe de mocks do rspec

No último artigo escrevi sobre a nova sintaxe do rspec, agora vou falar sobre a nova sintaxe para os mocks do rspec. Veja a documentação.

Apartir do rspec 2.14.0 as duas sintaxes estão presentes e você pode especificar qual deseja usar no spec_helper.

spec_helper.rb
RSpec.configure do |config|
  config.mock_with :rspec do |c|
    # c.syntax = [:expect, :should]
    # c.syntax = :should
    c.syntax = :expect
  end
  config.expect_with :rspec do |c|
    # c.syntax = [:should, :expect]
    # c.syntax = :should
    c.syntax = :expect
  end

Quando você alterar sua config deixando habilitado somente o :expect, muitos dos seus testes vão quebrar. Isso ocorre por que os métodos should_not_receive, should_receive, stub, stub_chain e unstub deixaram de existir.

Basicamente você precisa trocar o seu should_receive pelo expect().to receive e trocar o stub pelo allow().to receive, vamos ver alguns exemplos de como isso fica no código abaixo:

antes.rb
# should_receive
foo.should_receive(:bar)
foo.should_receive(:bar).with(1)
foo.should_receive(:bar).and_call_original

# should_not_receive
foo.should_not_receive(:bar)
foo.should_not_receive(:bar).with(1)
foo.should_not_receive(:bar).and_call_original

# stub
foo.stub(:bar)
foo.stub(:bar).and_call_original
antes.rb
# expect
expect(foo).to receive(:bar)
expect(foo).to receive(:bar).with(1)
expect(foo).to receive(:bar).and_call_original

# should_not_receive
expect(foo).to_not receive(:bar)
expect(foo).to_not receive(:bar).with(1)
expect(foo).to_not receive(:bar).and_call_original

# stub
allow(foo).to receive(:bar)
allow(foo).to receive(:bar).and_call_original

Observe que a diferença não foi tão grande na forma de escrever e os textos ficaram mais coeso porém na implementação, assim como na nova sintaxe do rspec, os metodos deixam de ser injetados em todos os objetos e passam a responder somente ao expect e o allow, o que torna a implementação muito mais simples.

Agora você deve estar perguntando: Ok, alterei o should_receive e os stub, mas e o stub_chain e o unstub?

Bom, como nas dicuções do rspec a respeito desta nova sintaxe o stub_chain não foi portado, por que não parece muito sensato ficarmos estubando vários métodos de uma única vez. Como eles mesmo colocaram "isso não cheira bem", então a melhor maneira de fazer isso é usando doubles.

antes.rb
foo.stub_chain(:bar, :size).and_return(1)
depois.rb
bar = double(:bar)
allow(bar).to receive(:size).and_return(1)
allow(foo).to receive(:bar).and_return(bar)

# or just
allow(foo).to receive(:bar).and_return(double(size: 1))

Já o unstub este não foi portado e realmente não deve ser usado. Se você estiver usando este método acredito que você possa refatorar seu teste pra não precisar mais dele, afinal pra que desfazer se você pode simplesmente não fazer?