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.