procedure TTestCalculadora.DivisaoDe6Por2DeveSer3; var vCalculadora: TCalculadora; begin vCalculadora := TCalculadora.Create; try CheckEquals(3, vCalculadora.Soma(6).DividePor(2).GetTotal, 'Divisão inválida'); finally vCalculadora.Free; end; end;Fail!
function TCalculadora.DividePor(const AValor: Double): TCalculadora; begin FValor := FValor / AValor; Result := Self; end;Success!
E o que acontece se eu tentar uma divisão por zero?
procedure TTestCalculadora.DivisaoDe6PorZero; var vCalculadora: TCalculadora; begin vCalculadora := TCalculadora.Create; try vCalculadora.Dividir(6, 0); finally vCalculadora.Free; end; end;Isso não parece bom. Rode o teste e veja que será lançada uma exceção do tipo EZeroDivide - se fosse um Integer lançaria uma EDivByZero - e o teste falhará.
Para esse tipo de teste, o DUnit oferece um recurso bastante útil, o ExpectedException, onde declaro que estou esperando uma exceção de determinado tipo. Se a exceção não for lançada, o teste falhará. Então vamos alterar o código e agora o mesmo deve passar.
procedure TTestCalculadora.DivisaoDe6PorZero; var vCalculadora: TCalculadora; begin ExpectedException := EZeroDivide; vCalculadora := TCalculadora.Create; try vCalculadora.Dividir(6, 0); finally vCalculadora.Free; end; end;
Esses testes nos trouxeram a tona um problema de design em nossa classe de teste, a duplicação, não estamos sendo DRY. Em cada teste implementado estamos instanciando e destruindo o objeto Calculadora. Vamos extrair isso para outros métodos.
A classe TTestCase da qual estamos herdando possue dois métodos adequados para o que precisamos: SetUp e TearDown. O SetUp é onde preparamento o teste - instanciar a calculadora. O TearDown é onde limpamos a sujeira - destruir a calculadora. É importante ressaltar que esses métodos são disparados para cada teste da suíte.
Refatorei a classe de teste, e ela ficou assim. Rode os testes e veja se continua tudo funcionando.
type TTestCalculadora = class(TTestCase) private FCalculadora: TCalculadora; protected procedure SetUp; override; procedure TearDown; override; published procedure SomaDe2Mais3DeveSer5; procedure DeveResetarOValor; procedure DivisaoDe6Por2DeveSer3; procedure DivisaoDe6PorZero; end; implementation uses SysUtils; { TTestCalculadora } procedure TTestCalculadora.SetUp; begin inherited; FCalculadora := TCalculadora.Create; end; procedure TTestCalculadora.TearDown; begin FCalculadora.Free; inherited; end; procedure TTestCalculadora.DeveResetarOValor; begin FCalculadora.Soma(1).Reset; CheckEquals(0, FCalculadora.GetTotal, 'Não resetou o valor'); end; procedure TTestCalculadora.DivisaoDe6Por2DeveSer3; begin CheckEquals(3, FCalculadora.Soma(6).DividePor(2).GetTotal, 'Divisão inválida'); end; procedure TTestCalculadora.DivisaoDe6PorZero; begin ExpectedException := EZeroDivide; FCalculadora.Soma(6).DividePor(0); end; procedure TTestCalculadora.SomaDe2Mais3DeveSer5; begin CheckEquals(5, FCalculadora.Soma(2).Soma(3).GetTotal); end;
Existem ainda varias outras opções de métodos de validação além do CheckEquals, alguns dos mais usados são
procedure CheckTrue(condition: Boolean; msg: string = ''); procedure CheckFalse(condition: Boolean; msg: string = ''); procedure CheckNotEquals(expected, actual: integer; msg: string = ''); procedure CheckNotNull(obj: TObject; msg: string = ''); procedure CheckNull(obj: TObject; msg: string = ''); procedure Fail(msg: sTring; ErrorAddrs: Pointer = nil);
O código fonte dos exemplos está disponível aqui. Até o próximo post, abraços e bons testes.
Parabéns pela colaboração, Fabricio. Sua contribuição é de grande importância a todos da comunidade Delphi.
ResponderExcluirAté mais!
Obrigado Paulo, fico feliz que tenha gostado. Fique ligado que em breve terá mais conteúdo novo.
ResponderExcluirAbraços.
Olá,
ResponderExcluirEm uma plicação real e de grande porte, que foi implementada sem o uso adequado de OO qual seria a melhor forma para adicionar testes automatizados?
Obrigado.