ti-enxame.com

Exemplo de objeto de simulação RSpec

Eu sou novo em objetos simulados e estou tentando aprender como usá-los no RSpec. Alguém pode postar um exemplo (um exemplo do tipo de objeto hello RSpec Mock para o mundo) ou um link (ou qualquer outra referência) sobre como usar a API de objeto simulado do RSpec?

37
agentbanks217

Aqui está um exemplo de uma simulação simples que fiz para um teste de controlador em um aplicativo Rails:

before(:each) do
  @page = mock_model(Page)
  @page.stub!(:path)
  @page.stub!(:find_by_id)
  @page_type = mock_model(PageType)
  @page_type.stub!(:name)
  @page.stub!(:page_type).and_return(@page_type)
end

Nesse caso, estou zombando dos modelos Page & PageType (Objects), bem como delineando alguns dos métodos que chamo.

Isso me dá a capacidade de executar testes como este:

it "should be successful" do
  Page.should_receive(:find_by_id).and_return(@page)
  get 'show', :id => 1
  response.should be_success
end

Eu sei que esta resposta é mais Rails específica, mas espero que ajude você um pouco.


Editar

Ok, então aqui está um exemplo de olá mundo ...

Dado o seguinte script (hello.rb):

class Hello
  def say
    "hello world"
  end
end

Podemos criar a seguinte especificação (hello_spec.rb):

require 'rubygems'
require 'spec'

require File.dirname(__FILE__) + '/hello.rb'

describe Hello do
  context "saying hello" do 
    before(:each) do
      @hello = mock(Hello)
      @hello.stub!(:say).and_return("hello world")
    end

    it "#say should return hello world" do
      @hello.should_receive(:say).and_return("hello world")
      answer = @hello.say
      answer.should match("hello world")
    end
  end
end
64
Brian

mock está obsoleto com base neste github pull .

Agora, em vez disso, podemos usar double - mais aqui ...

 before(:each) do
   @page = double("Page")
   end

  it "page should return hello world" do
    allow(@page).to receive(:say).and_return("hello world")
    answer = @page.say
    expect(answer).to eq("hello world")
  end
6
tokhi

Não tenho pontos suficientes para postar um comentário em uma resposta, mas queria dizer que a resposta aceita também me ajudou a tentar descobrir como stub com um valor aleatório.

Eu precisava ser capaz de stub o valor da instância de um objeto atribuído aleatoriamente, por exemplo:

class ClumsyPlayer < Player do

  def initialize(name, health = 100)
    super(name, health)
    @health_boost = Rand(1..10)
  end
end

Então, nas minhas especificações, tive um problema em descobrir como remover a saúde aleatória do jogador desajeitado para testar que, quando recebem uma cura, eles recebem o impulso adequado à sua saúde.

O truque foi:

@player.stub!(health_boost: 5)

De modo a stub! era a chave, eu estava apenas usando stub e ainda estava recebendo passadas e falhas aleatórias do rspec.

Então obrigado Brian

5
JoeManFoo

Normalmente, você deseja usar um Objeto Simulado quando deseja delegar alguma funcionalidade a outro objeto, mas não deseja testar a funcionalidade real no seu teste atual; portanto, substitua esse objeto por outro que seja mais fácil de controlar. Vamos chamar esse objeto de "dependência" ...

O que você está testando (objeto/método/função ...) pode interagir com essa dependência chamando métodos para ...

  • Consulta para alguma coisa.
  • Mude algo ou produza algum efeito colateral.

Ao chamar um método para consultar algo

Quando você está usando a dependência para "consultar" algo, não precisa usar a "API simulada" porque pode usar apenas um objeto comum e testar a saída esperada no objeto que está testando ... por exemplo:

describe "Books catalog" do
  class FakeDB
    def initialize(books:)
      @books = books
    end

    def fetch_books
      @books
    end
  end

  it "has the stored books" do
    db = FakeDB.new(books: ["Principito"])
    catalog = BooksCatalog.new(db)
    expect(catalog.books).to eq ["Principito"]
  end
end

Ao chamar um método para alterar algo ou produzir algum efeito colateral ...

Quando você deseja alterar sua dependência ou fazer algo com efeitos colaterais, como inserir um novo registro em um banco de dados, enviar um email, efetuar um pagamento, etc ... agora, em vez de testar se a alteração ou o efeito colateral foi produzido, você apenas verifica se está chamando a função/método certo com os atributos certos ... por exemplo:

describe "Books catalog" do
  class FakeDB
    def self.insert(book)
    end
  end

  def db
    FakeDB
  end

  it "stores new added books" do
    catalog = BooksCatalog.new(db)

    # This is how you can use the Mock API of rspec
    expect(db).to receive(:insert).with("Harry Potter")

    catalog.add_book("Harry Potter")
  end
end

Este é um exemplo básico, mas você pode fazer muito apenas com esse conhecimento =)

Eu escrevi um post com esse conteúdo e um pouco mais que talvez possa ser útil http://bhserna.com/2018/how-and-when-to-use-mock -objetos-com-Ruby-e-rspec.html

1
Benito Serna

O RSpec atual (3.x) fornece objetos de simulação puros (como em resposta de tokhi ) e simulação parcial (simulação de chamadas para um objeto existente). Aqui está um exemplo de zombaria parcial. Ele usa expect e receive para simular uma chamada de Order para um CreditCardService, para que o teste seja aprovado apenas se a chamada for feita sem a necessidade de efetivamente fazer isto.

class Order
  def cancel
     CreditCardService.instance.refund transaction_id
  end
end

describe Order do
  describe '#cancel' do
    it "refunds the money" do
      order = Order.new
      order.transaction_id = "transaction_id"
      expect(CreditCardService.instance).to receive(:refund).with("transaction_id")
      order.cancel
    end
  end
end

Neste exemplo, a simulação está no valor de retorno de CreditCardService.instance, que é presumivelmente um singleton.

with é opcional; sem ele, qualquer chamada para refund satisfaria a expectativa. Um valor de retorno pode ser fornecido com and_return; neste exemplo, ele não é usado; portanto, a chamada retorna nil.


Este exemplo usa a corrente atual do RSpec (expect .to receive) sintaxe de zombaria, que funciona com qualquer objeto. A resposta aceita usa o antigo rspec-Rails mock_model, que era específico dos modelos do ActiveModel e foi movido do rspec-Rails para outra jóia.

1
Dave Schweisguth