Quarta-feira, Dezembro 15, 2004

Test driven development visual

Estou começando a usar Test Driven Development e estou gostando muito. Para quem não sabe o que é isso, a grosso modo é o seguinte: paralelamente a cada classe criada, criamos outra classe para testar os métodos da classe principal. Assim, a cada mudança que fazemos na classe principal, rodamos os testes para verificar se não introduzimos algum bug. Isto é muito rápido e fácil e, uma vez que os testes estão criados, podemos executá-los praticamente a cada mudança feita, não deixando que os erros se propaguem.
No Delphi 2005, o DUnit e NUnit (frameworks de teste para Win32 e para .NET) são incluídos na instalação e, para as outras versões, pode-se baixar gratuitamente o DUnit em http://dunit.sourceforge.net/.
Para ilustrar, a coisa é mais ou menos assim: vamos imaginar uma classe



TCalc = class
public
function Soma(a, b : Double) : Double;
function Subtrai(a, b : Double) : Double;
end;

Criamos uma classe de teste assim:


TTestaCalc = class(TTestCase)
published
procedure TestaSoma;
procedure TestaSubtrai;
end;

A implementação destes métodos chama a função Check, que faz o teste:


procedure TTestaCalc.TestaSoma;
var
Calc : TCalc;
begin
Calc := TCalc.Create;
Check(Calc.Soma(1,2) = 3,'Erro ao somar');
Calc.Free;
end;

Quando rodamos os testes, a procedure TestaSoma é executada, testando a função Soma. Se introduzimos um erro no código, o teste aponta o erro.
Até aqui, tudo bem. Mas, e se precisamos fazer uma verificação visual. No meu caso, tenho que testar o desenho de sinal biológico na tela. Os dados devem ser mostrados nos locais corretos quando um determinado método de minha classe é executada. Como fazer isso? A primeira idéia foi criar uma Form com 2 botões, Ok e Errado, checando a condição de retorno. Os dados seriam mostrados na tela e eu faria a inspeção visual, clicando no botão selecionado. A idéia parece funcionar, mas imaginem rodar os testes a cada 10 minutos, tendo de inspecionar e clicar no botão. A partir da terceira vez, com certeza, este teste estaria desabilitado :-).
A idéia seguinte foi usar um bitmap. A coisa funciona assim: crio um bitmap padrão. A cada teste, crio um novo bitmap, leio o bitmap padrão e comparo os dois. Se houver uma diferença, o teste falha. Desta maneira, consegui fazer um teste automatizado para uma rotina essencialmente visual. A chave da coisa é que a sua rotina desenhe para um Canvas genérico, assim dá para direcionar para a Form ou para o bitmap. A rotina de teste ficou assim:


procedure TVisualTests.TestDesenha;
var
Bitmap, Padrao : TBitmap;
begin
// Cria um bitmap de 1100 x 200
Bitmap := TBitmap.Create;
Bitmap.Width := 1100;
Bitmap.Height := 200;
// FVisual é a instância da classe que está sendo testada
// ela é criada em Setup e destruída em TearDown
FVisual.Desenha(Bitmap.Canvas);
// Lê o bitmap padrão
Padrao := TBitmap.Create;
Padrao.LoadFromFile('padrao.bmp');
// Compara os dois no mesmo formato
Padrao.PixelFormat := pf24Bit;
Bitmap.PixelFormat := pf24Bit;
// O bitmap tem 200 linhas
for i := 0 to 199 do
// No formato 24bits, cada pixel ocupa 3 bytes,
// assim a ScanLine tem 3300 bytes
for j := 0 to 1099*3 do
// Comparo as scanlines byte a byte
Check(Byte(Padrao.ScanLine[i]^) =
Byte(Bitmap.ScanLine[i]^),
'Bitmaps não conferem');
Bitmap.Free;
Padrao.Free;
end;

0 Comentários:

Postar um comentário

<< Home