TDD em C++
Thiago Delgado Pinto
   Parte 1 – Verificação de Estado
   Parte 2 – Verificação de Comportamento
Verificação de Estado
YAFFUT_CHECK( ok );
YAFFUT_EQUAL( e, o );
YAFFUT_UNEQUAL( e, o );
YAFFUT_FAIL( msg );
YAFFUT_ASSERT_THROW( func, e );
   No Desenvolvimento Guiado por Testes, a
    corretude de um software é verificada
    através da escrita de testes para o mesmo.

   Nesses testes, verificamos se algo funciona
    conforme o esperado.

   Essa expectativa é o que devemos criar no
    teste.
   Uma expectativa é uma suposição sobre
    como algo funciona.

   Por exemplo:
    Se você quiser fazer uma função que some
    dois números naturais e retorne essa soma.
    Você pode supor que dados dois números, por
    exemplo, 1 e 2, a função retorne 3.
void somaDoisNumerosNaturaisCorretamente()
{                                            Nome do Teste
    assert( soma( 1, 2 ) == 3 );
}

         A função assert recebe um valor booleano.
         Se for true, a função não faz nada.
         Se for false, ela interrompe a execução do
         programa informando a linha em que houve a
         falha na afirmação.
void somaDoisNumerosInteirosCorretamente()
{
    assert( soma( -1, -1 ) == -2 );   Suposições devem
    assert( soma( -1, 0 ) == -1 );    tentar exercitar
                                      combinações com
    assert( soma( -1, 1 ) == 0 );     valores mínimos,
    assert( soma( 0, 0 ) == 0 );      máximos e
    assert( soma( 0, 1 ) == 1 );      potencialmente
                                      problemáticos.
    assert( soma( 1, 1 ) == 2 );
}
 Devemos, porém, dividir os casos em mais de
    um teste.
void somaUsandoNumerosNegativosCorretamente()
{
    assert( soma( -1, -1 ) == -2 );
    assert( soma( -1, 1 ) == 0 );
}
void somaComZeroResultaNoProprioNumero()
{
  assert( soma( -1, 0 ) == -1 );
  assert( soma( 0, 0 ) == 0 );
  assert( soma( 0, 1 ) == 1 );
}
void somaUsandoNumerosPositivosCorretamente()
{
  assert( soma( 1, 1 ) == 2 );
  assert( soma( 1, -1 ) == 0 );
}
void chuteAltoTiraUmDecimoDaEnergia()
{
    Lutador a, b;
    a.DefinirEnergia( 100 );
    b.DesferirChuteAltoEm( a );
    assert( a.Energia() == 90 );
}
void transferenciaFazDebitarDaContaOrigem()
{
    Conta origem, destino;
    origem.DefinirSaldo( 10500 );
    double valorATransferir = 500;
    double saldoEsperadoAposTransferencia =
      origem.Saldo() – valorATransferir;
    origem.Transferir( destino, valorATransferir );
    assert( origem.Saldo() ==
      saldoEsperadoAposTransferencia );
}
void transferenciaFazCreditarNaContaDestino()
{
    Conta origem, destino;
    origem.DefinirSaldo( 10500 );
    destino.DefinirSaldo( 2000 );
    double valorATransferir = 500;
    double saldoEsperadoAposTransferencia =
      destino.Saldo() + valorATransferir;
    origem.Transferir( destino, valorATransferir );
    assert( destino.Saldo() ==
      saldoEsperadoAposTransferencia );
}
   O nome deve tentar explicar o que é
    verificado e qual o resultado esperado.

   Nunca tente explicar como é verificado.

   O nome pode ser bem longo.

   Não se preocupe, você nunca precisará
    digitá-lo novamente. 
   ligarNitroFazCarroAcelerarQuandoEmMovimento ()

   acelerarComFreioDeMaoLigadoFazRodasTraseiras
    Derraparem()

   finalizarVendaFazBaixarEstoqueDeItensVendidos()

   cairNoPrecipicioRetiraVidaDoJogador()
   Precisamos criar um programa que chame todos
    os nossos testes.
   Há frameworks (bibliotecas de código que
    podem ser estendidas) que podemos usar para
    simplificar esse processo.
   Usando um framework, nosso main não irá
    precisar fazer nada além de iniciar o framework.
   O framework se encarregará de chamar os
    testes.
1/6



   Yet Another Framework For Unit Testing

   É opensource
   É portátil (Windows,GNU/Linux,MacOS,...)
   É pequeno
   É simples
   É poderoso

   http://members.home.nl/rutger.van.beusekom/
2/6


   Para usar o Yaffut, basta incluir seu único arquivo de código:
#include <yaffut.h>
   Criar uma classe (vazia) que servirá para agrupar os testes:
class TesteContaBancaria {};
   Use a macro TEST que recebe por parâmetro o nome da classe de
    teste e o nome do teste. Ex:
TEST( TesteContaBancaria,
    transferenciaFazDebitarDaContaOrigem )
{
  ...
}
3/6


   No lugar de assert, use YAFFUT_CHECK.
#include <yaffut.h>
class TesteContaBancaria {};
TEST( TesteContaBancaria, transferenciaFazDebitarDaContaOrigem )
{
  Conta origem, destino;
  origem.DefinirSaldo( 10500 );
    double valorATransferir = 500;
    double saldoEsperadoAposTransferencia =
      origem.Saldo() – valorATransferir;
    origem.Transferir( destino, valorATransferir );
    YAFFUT_CHECK( origem.Saldo() ==
     saldoEsperadoAposTransferencia );
}
4/6


   Porém, para comparar igualdades, prefira YAFFUT_EQUAL.
   Recebe dois parâmetros: valor esperado e valor obtido.

TEST( TesteContaBancaria,
  transferenciaFazDebitarDaContaOrigem )
{
  Conta origem, destino;
  origem.DefinirSaldo( 10500 );
    double valorATransferir = 500;
    double saldoEsperadoAposTransferencia =
      origem.Saldo() – valorATransferir;
    origem.Transferir( destino, valorATransferir );
    YAFFUT_EQUAL( saldoEsperadoAposTransferencia,
      origem.Saldo() );
}
5/6



   Outras funções:

YAFFUT_UNEQUAL( esperado, obtido )
YAFFUT_FAIL( mensagem )
YAFFUT_ASSERT_THROW( método, exceção )
6/6


   Como pode ser o programa principal de teste:
#include <yaffut.h>
#include “TesteContaBancaria.h”
// ... outras bibliotecas de teste
int main(int argc, const char* argv[])
{
  return yaffut::Factory::Instance().Main (argc,
   argv);
}
   CppUnit
   CppUnitLite
   boost::test
   TUT
   CxxTest
   ...
Verificação de Comportamento
MockRepository mr;
Intf *intf = mr.InterfaceMock< Intf >();
mr.ExpectCall( intf, Intf::Method1 )
 .With( “hello” )
 .Return( 10 );
AClass ac;
ac.DoSomething( *intf );
   TDD serve não só para verificar valores, mas,
    principalmente, para verificar o
    comportamento de objetos em suas
    interações com outros objetos.

   Para isso, criamos objetos substitutos,
    chamados de Mocks, que simulam a
    execução de métodos reais.
1/10



   Se ao fazer uma classe TerminalBancario
    quisermos ter um método que permita
    imprimir o extrato de uma conta bancária.

   Como testar se o extrato foi impresso ?
    (Só olhar o papel saindo, certo?)

   E se não tivermos impressora disponível para
    verificar ?
2/10



   Precisamos mesmo da impressora, ou
    podemos apenas simular o comportamento
    que se espera dela ?

   Se estamos interessados em seu
    comportamento, e não em sua
    implementação, podemos representar a
    impressora como uma interface.
3/10



class ImpressoraExtrato {
public:
   // Retorna true se conseguir imprimir
   bool Imprimir(const ContaBancaria
    &conta) = 0;
};
4/10


   Então, podemos pensar que nosso terminal
    bancário tenha o seguinte método:
// Retorna true se conseguir imprimir
bool TerminalBancario::ImprimirExtrato(
 const ContaBancaria &conta,
 const ImpressoraExtrato &impressora);
   Não importa como essa impressora funciona,
    desde que ela diga que imprimiu, para o terminal
    está OK.
5/10


   Daí, podemos criar uma implementação falsa da
    impressora, para ser nossa substituta da
    impressora real:
class ImpressoraExtratoFalsa : public
   ImpressoraExtrato
{
   bool Imprimir(const ContaBancaria &conta)
   {
     return true; // Só diz que imprimiu !
   }
};
6/10


   Nosso teste pode ficar assim:
TEST( TesteContaBancaria, terminalConsegueImprimirExtrato )
{
  ContaBancaria conta;
  conta.Depositar( 1000 );
  conta.Sacar( 500 );
    ImpressoraExtrato *impressora =
      new ImpressoraExtratoFalsa();
    TerminalBancario terminal;
    bool imprimiu = terminal.Imprimir(conta, *impressora );
    delete impressora;
    YAFFUT_CHECK( imprimiu );
}
7/10


   Mas como saber se o terminal interagiu
    corretamente com a impressora ?
   Sabemos apenas que a impressora foi passada
    como parâmetro, mas como saber se ela foi
    usada ?
   Ou seja, se o terminal chamou o método
    Imprimir como esperado (uma vez, passando a
    conta como parâmetro) e obteve um resultado
    dela ?
8/10



   Podemos, em implementação falsa, criar um
    controle para saber se o método foi chamado
    como esperado.

   E depois, no teste, verificamos se o método
    Imprimir foi chamado.
9/10


class ImpressoraExtratoFalsa : public ImpressoraExtrato
{
  ImpressoraExtratoFalsa() {
    chamadasAImprimir = 0;
  }
  bool Imprimir(const ContaBancaria &conta) {
    chamadasAImprimir++;
     return true;
  }
 int ChamadasAImprimir() const {
   return chamadasAImprimir;
 }
private:
   int chamadasAImprimir;
};
10/10


   Nosso teste pode ficar assim:
TEST( TesteContaBancaria, terminalConsegueImprimirExtrato )
{
  ContaBancaria conta;
  conta.Depositar( 1000 );
  conta.Sacar( 500 );
    ImpressoraExtrato *impressora =
      new ImpressoraExtratoFalsa();
    TerminalBancario terminal;
    bool imprimiu = terminal.Imprimir(conta, *impressora );
    YAFFUT_EQUAL( 1, impressora->ChamadasAImprimir() );
    YAFFUT_CHECK( imprimiu );
    delete impressora;
}
   Ter que criar manualmente implementações
    falsas (classes falsas) e contabilizar a execução
    de cada método é trabalhoso.
   Porém, há frameworks de teste que permitem a
    criação automática de implementações falsas
    (chamadas de Mocks), bastando apenas definir
    o comportamento esperado dos métodos.
   A contabilização da chamada também é feita
    automaticamente.
   Em C++ há bons frameworks de teste
        automatizado, porém, poucos com recursos de
        criação automática de mocks:
           Isolator++
           AMOP
           MockitoPP
           HippoMocks
           etc.

       Adotaremos o HippoMocks com Yaffut1.
1. Veja análise em http://devhints.blogspot.com/2010/11/bons-frameworks-c-para-criacao.html
   Feito usando Yaffut (já vem com ele).
   É opensource.
   É simples.
   É poderoso.
   É portátil.
   http://www.assembla.com/spaces/hippomocks
   Sua classe principal é MockRepository, que
    permite criar um repositório de objetos falsos
    (mocks).

   Permite criação de mocks através do método
    InterfaceMock.

   Permite definir expectativas através do
    método ExpectCall.
TEST( TesteContaBancaria, terminalConsegueImprimirExtrato )
{
  ContaBancaria conta;
  conta.Depositar( 1000 );
  conta.Sacar( 500 );

    MockRepository mr; // Repositório de mocks
    ImpressoraExtrato *impressora =
      mr.InterfaceMock< ImpressoraExtrato >(); // Cria um mock
    // Cria a expectativa
    mr.ExpectCall( impressora, ImpressoraExtrato::Imprimir )
      .With( conta )
      .Return( true );

    TerminalBancario terminal;
    bool imprimiu = terminal.Imprimir(conta, *impressora );
    YAFFUT_CHECK( imprimiu );
    delete impressora;
    // Não precisa verificar NADA.
    // O framework verifica se a expectativa criada foi atendida!
}
TEST( TesteBomba, atirarNumaBombaFazExplodiLa )
{
  MockRepository mr; // Repositório de objetos falsos
    // Bomba falsa
    Bomba *bomba = mr.InterfaceMock< Bomba >();
    // Expectativa do que deve acontecer com a bomba
    // ao ser acertada por um tiro
    mr.ExpectCall( bomba, Bomba::Explodir );

    Jogador jogador;
    Revolver revolver( 38 );
    jogador.SegurarObjeto( revolver );
    revolver.AtirarNoObjeto( bomba ); // Deve fazê-la explodir
}
TDD em C++
Thiago Delgado Pinto

TDD em C++

  • 1.
    TDD em C++ ThiagoDelgado Pinto
  • 2.
    Parte 1 – Verificação de Estado  Parte 2 – Verificação de Comportamento
  • 3.
    Verificação de Estado YAFFUT_CHECK(ok ); YAFFUT_EQUAL( e, o ); YAFFUT_UNEQUAL( e, o ); YAFFUT_FAIL( msg ); YAFFUT_ASSERT_THROW( func, e );
  • 4.
    No Desenvolvimento Guiado por Testes, a corretude de um software é verificada através da escrita de testes para o mesmo.  Nesses testes, verificamos se algo funciona conforme o esperado.  Essa expectativa é o que devemos criar no teste.
  • 5.
    Uma expectativa é uma suposição sobre como algo funciona.  Por exemplo: Se você quiser fazer uma função que some dois números naturais e retorne essa soma. Você pode supor que dados dois números, por exemplo, 1 e 2, a função retorne 3.
  • 6.
    void somaDoisNumerosNaturaisCorretamente() { Nome do Teste assert( soma( 1, 2 ) == 3 ); } A função assert recebe um valor booleano. Se for true, a função não faz nada. Se for false, ela interrompe a execução do programa informando a linha em que houve a falha na afirmação.
  • 7.
    void somaDoisNumerosInteirosCorretamente() { assert( soma( -1, -1 ) == -2 ); Suposições devem assert( soma( -1, 0 ) == -1 ); tentar exercitar combinações com assert( soma( -1, 1 ) == 0 ); valores mínimos, assert( soma( 0, 0 ) == 0 ); máximos e assert( soma( 0, 1 ) == 1 ); potencialmente problemáticos. assert( soma( 1, 1 ) == 2 ); }  Devemos, porém, dividir os casos em mais de um teste.
  • 8.
    void somaUsandoNumerosNegativosCorretamente() { assert( soma( -1, -1 ) == -2 ); assert( soma( -1, 1 ) == 0 ); } void somaComZeroResultaNoProprioNumero() { assert( soma( -1, 0 ) == -1 ); assert( soma( 0, 0 ) == 0 ); assert( soma( 0, 1 ) == 1 ); } void somaUsandoNumerosPositivosCorretamente() { assert( soma( 1, 1 ) == 2 ); assert( soma( 1, -1 ) == 0 ); }
  • 9.
    void chuteAltoTiraUmDecimoDaEnergia() { Lutador a, b; a.DefinirEnergia( 100 ); b.DesferirChuteAltoEm( a ); assert( a.Energia() == 90 ); }
  • 10.
    void transferenciaFazDebitarDaContaOrigem() { Conta origem, destino; origem.DefinirSaldo( 10500 ); double valorATransferir = 500; double saldoEsperadoAposTransferencia = origem.Saldo() – valorATransferir; origem.Transferir( destino, valorATransferir ); assert( origem.Saldo() == saldoEsperadoAposTransferencia ); }
  • 11.
    void transferenciaFazCreditarNaContaDestino() { Conta origem, destino; origem.DefinirSaldo( 10500 ); destino.DefinirSaldo( 2000 ); double valorATransferir = 500; double saldoEsperadoAposTransferencia = destino.Saldo() + valorATransferir; origem.Transferir( destino, valorATransferir ); assert( destino.Saldo() == saldoEsperadoAposTransferencia ); }
  • 12.
    O nome deve tentar explicar o que é verificado e qual o resultado esperado.  Nunca tente explicar como é verificado.  O nome pode ser bem longo.  Não se preocupe, você nunca precisará digitá-lo novamente. 
  • 13.
    ligarNitroFazCarroAcelerarQuandoEmMovimento ()  acelerarComFreioDeMaoLigadoFazRodasTraseiras Derraparem()  finalizarVendaFazBaixarEstoqueDeItensVendidos()  cairNoPrecipicioRetiraVidaDoJogador()
  • 14.
    Precisamos criar um programa que chame todos os nossos testes.  Há frameworks (bibliotecas de código que podem ser estendidas) que podemos usar para simplificar esse processo.  Usando um framework, nosso main não irá precisar fazer nada além de iniciar o framework.  O framework se encarregará de chamar os testes.
  • 15.
    1/6  Yet Another Framework For Unit Testing  É opensource  É portátil (Windows,GNU/Linux,MacOS,...)  É pequeno  É simples  É poderoso  http://members.home.nl/rutger.van.beusekom/
  • 16.
    2/6  Para usar o Yaffut, basta incluir seu único arquivo de código: #include <yaffut.h>  Criar uma classe (vazia) que servirá para agrupar os testes: class TesteContaBancaria {};  Use a macro TEST que recebe por parâmetro o nome da classe de teste e o nome do teste. Ex: TEST( TesteContaBancaria, transferenciaFazDebitarDaContaOrigem ) { ... }
  • 17.
    3/6  No lugar de assert, use YAFFUT_CHECK. #include <yaffut.h> class TesteContaBancaria {}; TEST( TesteContaBancaria, transferenciaFazDebitarDaContaOrigem ) { Conta origem, destino; origem.DefinirSaldo( 10500 ); double valorATransferir = 500; double saldoEsperadoAposTransferencia = origem.Saldo() – valorATransferir; origem.Transferir( destino, valorATransferir ); YAFFUT_CHECK( origem.Saldo() == saldoEsperadoAposTransferencia ); }
  • 18.
    4/6  Porém, para comparar igualdades, prefira YAFFUT_EQUAL.  Recebe dois parâmetros: valor esperado e valor obtido. TEST( TesteContaBancaria, transferenciaFazDebitarDaContaOrigem ) { Conta origem, destino; origem.DefinirSaldo( 10500 ); double valorATransferir = 500; double saldoEsperadoAposTransferencia = origem.Saldo() – valorATransferir; origem.Transferir( destino, valorATransferir ); YAFFUT_EQUAL( saldoEsperadoAposTransferencia, origem.Saldo() ); }
  • 19.
    5/6  Outras funções: YAFFUT_UNEQUAL( esperado, obtido ) YAFFUT_FAIL( mensagem ) YAFFUT_ASSERT_THROW( método, exceção )
  • 20.
    6/6  Como pode ser o programa principal de teste: #include <yaffut.h> #include “TesteContaBancaria.h” // ... outras bibliotecas de teste int main(int argc, const char* argv[]) { return yaffut::Factory::Instance().Main (argc, argv); }
  • 21.
    CppUnit  CppUnitLite  boost::test  TUT  CxxTest  ...
  • 22.
    Verificação de Comportamento MockRepositorymr; Intf *intf = mr.InterfaceMock< Intf >(); mr.ExpectCall( intf, Intf::Method1 ) .With( “hello” ) .Return( 10 ); AClass ac; ac.DoSomething( *intf );
  • 23.
    TDD serve não só para verificar valores, mas, principalmente, para verificar o comportamento de objetos em suas interações com outros objetos.  Para isso, criamos objetos substitutos, chamados de Mocks, que simulam a execução de métodos reais.
  • 24.
    1/10  Se ao fazer uma classe TerminalBancario quisermos ter um método que permita imprimir o extrato de uma conta bancária.  Como testar se o extrato foi impresso ? (Só olhar o papel saindo, certo?)  E se não tivermos impressora disponível para verificar ?
  • 25.
    2/10  Precisamos mesmo da impressora, ou podemos apenas simular o comportamento que se espera dela ?  Se estamos interessados em seu comportamento, e não em sua implementação, podemos representar a impressora como uma interface.
  • 26.
    3/10 class ImpressoraExtrato { public: // Retorna true se conseguir imprimir bool Imprimir(const ContaBancaria &conta) = 0; };
  • 27.
    4/10  Então, podemos pensar que nosso terminal bancário tenha o seguinte método: // Retorna true se conseguir imprimir bool TerminalBancario::ImprimirExtrato( const ContaBancaria &conta, const ImpressoraExtrato &impressora);  Não importa como essa impressora funciona, desde que ela diga que imprimiu, para o terminal está OK.
  • 28.
    5/10  Daí, podemos criar uma implementação falsa da impressora, para ser nossa substituta da impressora real: class ImpressoraExtratoFalsa : public ImpressoraExtrato { bool Imprimir(const ContaBancaria &conta) { return true; // Só diz que imprimiu ! } };
  • 29.
    6/10  Nosso teste pode ficar assim: TEST( TesteContaBancaria, terminalConsegueImprimirExtrato ) { ContaBancaria conta; conta.Depositar( 1000 ); conta.Sacar( 500 ); ImpressoraExtrato *impressora = new ImpressoraExtratoFalsa(); TerminalBancario terminal; bool imprimiu = terminal.Imprimir(conta, *impressora ); delete impressora; YAFFUT_CHECK( imprimiu ); }
  • 30.
    7/10  Mas como saber se o terminal interagiu corretamente com a impressora ?  Sabemos apenas que a impressora foi passada como parâmetro, mas como saber se ela foi usada ?  Ou seja, se o terminal chamou o método Imprimir como esperado (uma vez, passando a conta como parâmetro) e obteve um resultado dela ?
  • 31.
    8/10  Podemos, em implementação falsa, criar um controle para saber se o método foi chamado como esperado.  E depois, no teste, verificamos se o método Imprimir foi chamado.
  • 32.
    9/10 class ImpressoraExtratoFalsa :public ImpressoraExtrato { ImpressoraExtratoFalsa() { chamadasAImprimir = 0; } bool Imprimir(const ContaBancaria &conta) { chamadasAImprimir++; return true; } int ChamadasAImprimir() const { return chamadasAImprimir; } private: int chamadasAImprimir; };
  • 33.
    10/10  Nosso teste pode ficar assim: TEST( TesteContaBancaria, terminalConsegueImprimirExtrato ) { ContaBancaria conta; conta.Depositar( 1000 ); conta.Sacar( 500 ); ImpressoraExtrato *impressora = new ImpressoraExtratoFalsa(); TerminalBancario terminal; bool imprimiu = terminal.Imprimir(conta, *impressora ); YAFFUT_EQUAL( 1, impressora->ChamadasAImprimir() ); YAFFUT_CHECK( imprimiu ); delete impressora; }
  • 34.
    Ter que criar manualmente implementações falsas (classes falsas) e contabilizar a execução de cada método é trabalhoso.  Porém, há frameworks de teste que permitem a criação automática de implementações falsas (chamadas de Mocks), bastando apenas definir o comportamento esperado dos métodos.  A contabilização da chamada também é feita automaticamente.
  • 35.
    Em C++ há bons frameworks de teste automatizado, porém, poucos com recursos de criação automática de mocks:  Isolator++  AMOP  MockitoPP  HippoMocks  etc.  Adotaremos o HippoMocks com Yaffut1. 1. Veja análise em http://devhints.blogspot.com/2010/11/bons-frameworks-c-para-criacao.html
  • 36.
    Feito usando Yaffut (já vem com ele).  É opensource.  É simples.  É poderoso.  É portátil.  http://www.assembla.com/spaces/hippomocks
  • 37.
    Sua classe principal é MockRepository, que permite criar um repositório de objetos falsos (mocks).  Permite criação de mocks através do método InterfaceMock.  Permite definir expectativas através do método ExpectCall.
  • 38.
    TEST( TesteContaBancaria, terminalConsegueImprimirExtrato) { ContaBancaria conta; conta.Depositar( 1000 ); conta.Sacar( 500 ); MockRepository mr; // Repositório de mocks ImpressoraExtrato *impressora = mr.InterfaceMock< ImpressoraExtrato >(); // Cria um mock // Cria a expectativa mr.ExpectCall( impressora, ImpressoraExtrato::Imprimir ) .With( conta ) .Return( true ); TerminalBancario terminal; bool imprimiu = terminal.Imprimir(conta, *impressora ); YAFFUT_CHECK( imprimiu ); delete impressora; // Não precisa verificar NADA. // O framework verifica se a expectativa criada foi atendida! }
  • 39.
    TEST( TesteBomba, atirarNumaBombaFazExplodiLa) { MockRepository mr; // Repositório de objetos falsos // Bomba falsa Bomba *bomba = mr.InterfaceMock< Bomba >(); // Expectativa do que deve acontecer com a bomba // ao ser acertada por um tiro mr.ExpectCall( bomba, Bomba::Explodir ); Jogador jogador; Revolver revolver( 38 ); jogador.SegurarObjeto( revolver ); revolver.AtirarNoObjeto( bomba ); // Deve fazê-la explodir }
  • 40.
    TDD em C++ ThiagoDelgado Pinto