RSpec – Objetos Mock com comportamento de Objetos Reais

Posted by Herminio on agosto 25, 2009

Objetos Mock com comportamento de Objetos Reais

RSpec permite que você adicione o comportamento de objetos reais para objetos fictícios, para que possa definir as expectativas de mensagens ocultas e método em qualquer objeto em seu sistema.

Uma utilização comum deste apoio é isolar exemplos do ActiveRecord e, portanto, o banco de dados em exemplos no Ruby on Rails.

    MyModel.should_receive(:find).with(id).and_return(@mock_model_instance)

Controlar o comportamento dos métodos da classe de nível (por exemplo, tendo-lhes que devolver um objeto fictício, em vez de uma instância real da classe modelo) permite-lhe descrever seus controllers e views de forma isolada a partir da lógica de ocorrência de classes do seu model. Isso significa que você pode mudar as regras de validação de um modelo, por exemplo, e de unidade que nos exemplos modelos sem afetar o tratamento e exemplos de exibição.

Isso também ajuda a manter o contexto do seu exemplo completamente em exibição (sem ter de olhar para fixtures/xyz.yml e ter que entender o que está acontecendo).

RSpec – Mocks and Stubs

Posted by Herminio on agosto 25, 2009

Mocks and Stubs

Objetos Mock são objetos de dublê que dão maior controle declarativa sobre o seu comportamento no cursor da execução de um exemplo. Definindo mensagens de expectativas e métodos stubs de objetos permite que você especifique uma simulação como um objeto colaborar/corresponder com os outros antes desses objetos existirem.

Você também pode usar objetos dublê para isolar os seus exemplos de serviços que são complexos para configurar ou caros demais para serem executados e assim pode manter seu conjunto de exemplos funcionando rapidamente.

RSpec possibilita ter objetos falsos, assim lhe permite criar objetos fictícios em seus exemplos de objetos mocks, tendo o mesmo comportamento de seus objetos existentes.

Criando um objeto fictício

Você cria um objeto mock com o método de simulação:

    my_mock = mock(name)

Isso cria uma nova simulação com esse name (uma string) e registra-lo. Quando terminar o exemplo, todas as simulações registradas são verificadas.

    my_mock = mock(name, stubs_and_options)

Como acima, mas permite que você defina valores de retorno mínimo para determinadas mensagens e opções à ajustar a simulação do comportamento. Atualmente a única opção suportada é a seguinte :null_object. Definir isso como verdade instrui a ironizar a ignorar (baixo consumo) qualquer mensagem que não foi dita para esperar e retornar-se.

    my_mock = mock("blah", :null_object => true)

Notas
Há muitos pontos de vista diferentes sobre o significado de mocks e stubs. Se você estiver interessado em aprender mais, aqui estão algumas leituras recomendadas:

• Mock Objects: http://www.mockobjects.com/
• Endo-Testing: http://www.mockobjects.com/files/endotesting.pdf
• Mock Roles, Not Objects: http://www.mockobjects.com/files/mockrolesnotobjects.pdf
• Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html
• Mocks aren’t stubs: http://www.martinfowler.com/articles/mocksArentStubs.html

RSpec – Expectativas

Posted by Herminio on agosto 25, 2009

Spec::Expectations e Spec::Matchers

Quando executa especificações do RSpec, que define #should e #should_not a
cada objeto do sistema. Esses métodos são a sua entrada para a magia do RSpec.

Veja Spec::Expectations e Spec::Matchers para mais informações.

RSpec – Exemplos de código executável

Posted by Herminio on agosto 24, 2009

Exemplos de código executável

RSpec fornece uma Domain Specific Language(DSL) com o qual você pode expressar exemplos executável do comportamento esperado de seu código.

Imagine que você estivesse falando com um cliente solicitando o software para o seu banco. Parte da conversa que bem poderia ser assim:

    Você: Descrever uma conta quando ela é criada pela primeira vez.
    Cliente: Ele deve ter um saldo de $0.

Veja como podemos expressar essa conversa de RSpec:

    describe Account, "when first created" do
    it "should have a balance of $0" do
        …
        end
    end

O describe() é um método que retorna uma classe ExampleGroup, que contém um conjunto de exemplos de um comportamento específico do sistema que pretende descrever.

O it() é um método que retorna uma instância da ExampleGroup em que esse exemplo é executado.

Quando você executar esse exemplo, RSpec pode fornecer um relatório como este:

    Conta, quando criada pela primeira vez
    - Deve ter um saldo de $0

before() e after()

Você pode usar before() e/ou after() para definir um código que é executado antes e depois de cada exemplo, ou apenas uma vez por exampleGroup:

    describe Thing do
      before(:all) do
            # Este é executado apenas uma única vez, antes de todos os exemplos
            # e antes de qualquer blocks before(:each)
      end

      before(:each) do
        # Este é executado antes de cada exemplo
      end

      before do
            # :each é o padrão, então este é o mesmo que before(:each)
      end

      it "should do stuff" do
            # deve fazer coisas
        …
      end

      it "should do more stuff" do
            # deve fazer mais coisas
        …
      end

      after(:each) do
        # isto é, após cada exemplo
      end

      after do
            # :each é o padrão, então este é o mesmo que after(:each)
      end

      after(:all) do
            # Este é executado apenas uma única vez depois de todos os exemplos
            # E depois de qualquer blocks after(:each)
      end
    end

Atenção: A utilização do before(:all) e after(:all) é desencorajada porque ela introduz dependências entre os exemplos. Mesmo assim, pode ser útil para operações se você sabe o que está fazendo.

Métodos Helper

Você pode escrever métodos helper(auxiliares) diretamente dentro de um Exemple Group:

    describe "…" do
      it "…" do
        helper_method
      end

      def helper_method
        …
      end
    end

Métodos Helper Reutilizáveis

Você pode incluir métodos helper em vários ExampleGroups, basta colocar dentro de um módulo e em seguida, incluindo um módulo em seu exampleGroup:

    module AccountExampleHelperMethods
      def helper_method
        …
      end
    end

    describe "A new account" do
      include AccountExampleHelperMethods
      before do
        @account = Account.new
      end

      it "should have a balance of $0" do
        helper_method
        @account.balance.should eql(Money.new(0, :dollars))
      end
    end

Exemplo Grupos Compartilhados

Você pode criar exemplo de grupos compartilhados e incluir os grupos em outros grupos.

Suponha que você tenha algum tipo de comportamento que se aplica a todas as edições do seu produto, grandes e pequenos.

Primeiro comportamento do fator de “compartilhamento”:

    shared_examples_for "all editions" do
      it "should behave like all editions" do
      end
    end

Então, quando você precisa definir o comportamento para as grandes e pequenas edições,
referência o comportamento compartilhado usando o método it_should_behave_like ()

    describe "SmallEdition" do
      it_should_behave_like "all editions"

      it "should also behave like a small edition" do
      end
    end

    describe "LargeEdition" do
      it_should_behave_like "all editions"

      it "should also behave like a large edition" do
      end
    end

it_should_behave_like irá procurar um ExampleGroup pela sua descrição, neste caso, “all editions”

Todas abaixo são incluídas em um grupo compartilhado:
* before(:all)
* before(:each)
* after(:each)
* after(:all)
* all included modules
* all methods

Exemplos de grupos compartilhados não podem extend(estender) as classes.

Vários exemplos grupos compartilhados pode ser referenciado em um grupo não compartilhado.

Exemplos de grupos compartilhados podem ser incluídos em outros grupos compartilhados:

    shared_examples_for "All Employees" do
      it "should be payable" do
        @employee.should respond_to(:calculate_pay)
      end
    end

    shared_examples_for "All Managers" do
      it_should_behave_like "All Employees"

      it "should be bonusable" do
        @employee.should respond_to(:apply_bonus)
      end
    end

    describe Officer do
      before(:each) do
        @employee = Officer.new
      end

      it_should_behave_like "All Managers"

      it "should be optionable" do
        @employee.should respond_to(:grant_options)
      end
    end


    $ spec officer_spec.rb
    Officer
    - should be payable
    - should be bonusable
    - should be optionable

Exemplos Pendentes

Há três formas de marcar um exemplo como “pendente”.
Deixar o bloco:

    it "should say foo"

A saída será dizer pendente (ainda não implementado).
Use o método pendente com nenhum bloco:

    it "should say foo" do
      pending("get the vocal chords working")
      subject.should say("foo")
    end

A saída será dizer PENDENTES (obter as cordas vocais de trabalho).
Use o método pendente com um bloco:

    it "should say foo" do
      pending("get the vocal chords working") do
        subject.should say("foo")
      end
    end

A saída será dizer PENDENTES (obter as cordas vocais de trabalho), e o exemplo falhará dizendo-lhe para que seja corrigido logo o bloco e não gera nenhum erro.

RSpec – O Inicio, como testar com rspec.

Posted by Herminio on agosto 24, 2009

Primeiramente vamos instalar Gem Spec.

$ gem install rspec

Agora vamos criar a nossa estrutura onde vamos trabalhar um exemplo simples.

$ mkdir bowling
$ cd bowling
$ mkdir spec
$ mkdir lib

Comece com um exemplo muito simples que expressa algum comportamento básico desejado.

# vamos criar um arquivo chamado bowling_spec.rb
# dentro pasta spec que criamos agora a pouco
require 'rubygems'
# o require a rubygems é para que eu possa
# chamadar a gem spec e testar minha aplicação.
require 'spec'
# e este require é do meu arquivo de boliche
# onde vou tentar validar seu código, com testes.
require 'lib/bowling'

# aqui é onde vamos descrever um boliche
describe Bowling do

    # vamos descrever um comportamento
    # deve ter 0 escore na calha do jogo
    it "should score 0 for gutter game" do
        # cria um novo objeto
        bowling = Bowling.new
        # passa o valor 0 pinos para o método hit
        20.times { bowling.hit(0) }
        # aqui ele diz: score deveria ser igual a 0
        bowling.score.should == 0
    end

end

Vamos executar o teste e vê-lo falhar.

$ spec spec/bowling_spec.rb --format specdoc
./spec/bowling_spec.rb:11: uninitialized constant Bowling

Agora, vamos escrever o código apenas o suficiente para fazê-lo passar.

# bowling - boliche

class Bowling

    def hit(pins)
    end

    def score
        0
    end

end

Execute o exemplo novamente e veja o teste passar.

$ spec spec/bowling_spec.rb --format specdoc

Bowling
- should score 0 for gutter game

Finished in 0.002854 seconds

1 example, 0 failures

Tomar medidas pequenas(baby-steps)
Não se apresse em escrever muito código. Em vez disso, adicionar outro exemplo, e deixá-lo guiar dizendo o que você tem que fazer a seguir. E não se esqueça de ter tempo para refatorar seu código antes que ele fique confuso. Você deve manter seu código limpo, a cada passo dado no seu caminho.