O Problema
Considere o seguinte problema. Não passe adiante apressadamente. Tome uns 10 minutinhos pra considerar o seguinte:
Desenhe um diagrama UML ou simplemente aliste as classes que você precisaria pra fazer a seguinte aplicação.
Nós queremos fazer um Jogo da Velha neste exercício. Existem dois jogadores que devem utilizar a aplicação. Um coloca a marca “X” e o outro o “O” na casa desejada. Existem três linhas e três colunas. Primeiro, o usuário deve indicar se o primeiro jogador irá colocar o “X” ou o “O”. Depois, o jogador inicial irá começar marcando a primeira casa. O jogador só poderá utilizar uma casa vazia. Os jogadores se alternam e o jogo continua até que um jogador tenha colocado três marcas iguais em uma linha, diagonal ou coluna ou até que não haja mais nenhuma casa vazia (condição de empate). Se um dos jogadores ganha, o JogoDaVelha deve anunciar isto indicando o nome do ganhador. A aplicação deverá controlar os scores (número de vezes que cada jogador ganhou). A qualquer momento o jogador pode querer ver as estatísticas do jogo.
Gastou seus 10 minutos pensando em como fazer este pequeno jogo? Então, agora pode continuar...
Pensando em classes
Quais as classes necessárias pra desenvolver esta pequena aplicação? Pense um pouco... Alguns diriam Tabuleiro, Casa, Marca, Jogador, Score, etc. E você, quais as classes que você acha necessário neste jogo?
Como escrever os testes
Primeiro pense no que seria necessário testar. Existem pelo menos três coisas para as quais você precisa escrever testes: positivo, negativo e exceção. Quando estiver pensando em uma tarefa, pense do lado positivo, ou seja, o que a aplicação deveria fazer corretamente se tudo estiver na situação ideal. Então, pense pelo lado negativo, ou seja, o que poderia dar errado e como a aplicação deveria se comportar. A exceção é uma seqüência alternativa de acontecimentos. Pense em como o código deve se comportar nestes casos. O sucesso de um teste positivo é quando o código se comporta como deveria. O sucesso de um teste negativo ou exceção é quando o código se comporta de forma esperada à uma situação negativa ou de exceção.
Onde escrever os testes
Uma vez que não estejamos interessados em testar apenas os métodos públicos de uma classe, mas também os métodos internos (private), devemos escrevê-los dentro da mesma unit, para que tenhamos acesso aos metodos e dados internos da classe. Se este não for o caso, podemos escrever em outra unit ou até mesmo em outro projeto.
Lista de tarefas
O primeiro passo é escrever uma lista de tarefas. Esta lista conterá um ou mais testes. Então, à medida que formos escrevendo os testes, implementando o código e sendo bem sucedidos, vamos marcando cada item da lista e passando pro próximo item. Enquanto escrevemos os testes e o código, pensaremos em outros testes que precisamos fazer. Mas em vez de escrever os testes enquanto eles vêm à mente, coloque-os no fim da lista de tarefas. É importante continuar a trabalhar nos itens da lista que já tem à mao. Então, você pode voltar e dar-lhes a devida atenção.
Podemos começar a codificar, por favor?
Claro, podemos iniciar. Mas, antes, tome o cuidade de providenciar a lista de tarefas. O que devemos testar primeiro? Que tal criar o tabuleiro? Ok. Mas o que fazer depois de criar o tabuleiro? Bem, sempre devemos escrever os nossos testes com as assertivas em mente. Temos que fazer os testes com assertivas para ver se a criação da nossa classe foi bem sucedida. Vamos então à lista de tarefas:
1. Criar Tabuleiro;
Em primeiro lugar, criamos um projeto em branco chamado JogoDaVelha.dpr, no qual daremos suporte ao teste unitário como se segue (observe que não criamos nenhum form ainda, pois iniciaremos o desenvolvimento pelos testes):
program JogoDaVelha;
uses
Forms,
TestFramework,
GUITestRunner;
{$R *.res}
begin
{$IFDEF TESTING}
GUITestRunner.RunRegisteredTests;
{$ELSE}
Application.Initialize;
Application.Run;
{$ENDIF}
end.
Listagem 1.
Você precisará ter instalado o dunit (9.3 ou mais recente) para executar os testes. Nas opções do projeto ativaremos os testes unitários definindo a diretiva TESTING em conditional defines. Quando esta diretiva estiver definida, o sistema irá rodar no modo de testes, caso contrário irá rodar o nosso aplicativo normalmente. Veja na figura abaixo como fica:
Figura 1.
Os testes são classes especializadas herdadas de TTestCase. Estes testes devem ser registrados para que o Framework de testes tenha conhecimento que ele existe. Utilizamos a sessão initialization para registrar os testes. Devemos nos lembrar de referenciar a unit TestFramework na cláusula uses. Como você deve ter notado na listagem 1, quando definimos a diretiva de compilação TESTING, o sistema irá executar os testes que estiverem registrados:
GUITestRunner.RunRegisteredTests;
Agora vamos escrever o nosso primeiro teste, que residirá na mesma unit da nossa classe para que possamos testar os membros privados da classe. Pra isso criamos uma unit que chamaremos de uTabuleiro.pas e iniciaremos a codificação pelo teste, como se segue:
unit uTabuleiro;
interface
uses
SysUtils, Classes;
// A definição de nossa classe tabuleiro vem aki
// TODO: Criar classe TTabuleiro
implementation
uses TestFramework;
// *************************************************
// Esta é a classe que efetuará os testes unitários
// *************************************************
type
TTabuleiroJVTests = class(TTestCase)
protected
procedure SetUp; override;
procedure TearDown; override;
end;
{ TTabuleiroTests }
// *****************************************************************************
// Este método é chamado no início de cada teste
// pode ser usado pra configurar o teste, criar variáveis de instância, etc.
// *****************************************************************************
procedure TTabuleiroJVTests.SetUp;
begin
inherited;
end;
// *****************************************************************************
// Este método é chamado no fim de cada teste
// pode ser usado pra liberar os recursos, entre outras coisas
// *****************************************************************************
procedure TTabuleiroJVTests.TearDown;
begin
inherited;
end;
initialization
TestFramework.RegisterTest('TTabuleiroJV Tests', TTabuleiroJVTests.Suite);
end.
Listagem 2.
Execute (F9) o sistema para ver se está tudo ok. Se estiver devemos ver uma tela como esta:
Bem, agora que o nosso teste está configurado, registrado e funcional podemos iniciar a codificação do primeiro item da lista (“criar tabuleiro”):
type
TTabuleiroJVTests = class(TTestCase)
protected
procedure SetUp; override;
procedure TearDown; override;
published // Os testes devem ficar na sessão published
procedure TestCriarTabuleiro;
end;
procedure TTabuleiroTests.TestCriarTabuleiro;
var
lTabuleiro: TTabuleiroJV;
begin
lTabuleiro := TTabuleiroJV.Create;
CheckNotNull(lTabuleiro);
CheckFalse(lTabuleiro.GameOver);
lTabuleiro.Free;
end;
Se tentarmos compilar a nossa aplicação agora receberemos uma mensagem de erro, afinal ainda nem existe uma classe chamada TTabuleiroJV. Criamos a classe TTabuleiroJV com uma propriedade GameOver:
type
TTabuleiroJV = class
private
function GetGameOver: Boolean;
public
property GameOver: Boolean read GetGameOver;
end;
implementation
{ TTabuleiroJV }
function TTabuleiroJV.GetGameOver: Boolean;
begin
Result := False; // Neste ponto implementamos apenas o que for estritamente necessário
// para o teste passar
end;
Quando executamos novamente e mandamos executar os testes selecionados, obtemos o seguinte resultado:
Figura 2.
Legal, o nosso teste passou! Agora, diante do resultado positivo, ficamos encorajados a continuar. O que vem em seguida? Que outros testes devemos fazer? Vamos incrementar a nossa lista de tarefas:
2. Configurar o primeiro jogador;
3. Configurar o primeiro jogador novamente;
4. Configurar o primeiro jogador depois que o jogo é iniciado;
Nós não queremos apenas testar a configuração do primeiro jogador, mas também pensar sobre o que aconteceria caso tentássemos configurá-lo novamente ou durante o jogo em andamento.
Agora pense um pouco: Como devemos implementar o nosso teste #2?
· Que tal tabuleiro.SetPrimeiroJogador(‘Marcos’)?
· Será relevante pro jogo saber que o jogador A é Marcos?
· Pode ser que sim, pode ser que não. Não temos certeza neste momento;
· Por isso vamos nos lembrar do princípio que diz: “Não faça algo que não tem certeza que irá precisar naquele momento”
Então podemos tentar:
tabuleiro.SetPrimeiroJogadorJogaX(true);
Com isto não precisamos nos preocupar sobre os testes nem sobre o jogador usado. E, como temos apenas duas opções (“X” e “O”) podemos usar um boolean pra indicar se o Jogador A (Que ainda nem sabemos quem é...) irá jogar “X” ou não. Mais fácil, não? Bem, uma vez decidido como será o teste, vamos implementá-lo:
type
TTabuleiroJVTests = class(TTestCase)
protected
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestCriarTabuleiro;
procedure TestSetPrimeiroJogador;
end;
procedure TTabuleiroJVTests.TestSetPrimeiroJogador;
var
lTabuleiro: TTabuleiroJV;
begin
lTabuleiro := TTabuleiroJV.Create;
lTabuleiro.PrimeiroJogadorJogaX := True;
CheckTrue(lTabuleiro.PrimeiroJogadorJogaX);
lTabuleiro.Free;
end;
Quando tentamos compilar recebemos uma mensagem de erro indicando que a propriedade PrimeiroJogadorJogaX não existe, por isso iremos implementá-la:
TTabuleiroJV = class
public
...
property PrimeiroJogadorJogaX: Boolean read GetPrimeiroJogadorJogaX
write SetPrimeiroJogadorJogaX;
end;
function TTabuleiroJV.GetPrimeiroJogadorJogaX: Boolean;
begin
Result := True;
end;
procedure TTabuleiroJV.SetPrimeiroJogadorJogaX(const Value: Boolean);
begin
end;
Lembre-se: Não iremos implementar nada que não seja estritamente necesário pra passar no teste.
Agora executamos o teste novamente e: Tudo verdin... que lindo! E visto que foi bem sucedido, ticamos o teste #2 na nossa lista de tarefas:
3. Configurar o primeiro jogador novamente;
4. Configurar o primeiro jogador depois que o jogo é iniciado;
Agora vamos pro terceiro teste. O que acontece caso tente indicar o primeiro jogador novamente, talvez com um valor diferente? Vamos ao teste:
type
TTabuleiroJVTests = class(TTestCase)
...
published
...
procedure TestSetPrimeiroJogadorNovamente;
end;
procedure TTabuleiroJVTests.TestSetPrimeiroJogadorNovamente;
var
lTabuleiro: TTabuleiroJV;
begin
lTabuleiro := TTabuleiroJV.Create;
lTabuleiro.PrimeiroJogadorJogaX := True;
CheckTrue(lTabuleiro.PrimeiroJogadorJogaX);
lTabuleiro.PrimeiroJogadorJogaX := False;
CheckFalse(lTabuleiro.PrimeiroJogadorJogaX);
lTabuleiro.Free;
end;
Compilamos, tudo ok... Agora vamos executar o teste:
O que aconteceu???! Por que falhou? Falhou porque esperávamos False mas lTabuleiro.PrimeiroJogadorJogaX retornou True. Olhando o código podemos ver o motivo da falha. Ainda não implementamos a propriedade corretamente. Ela vai retornar sempre True. Então mãos à obra. Vamos implementá-la corretamente agora, introduzindo uma variável de instância pra armazenar o valor:
type
TTabuleiroJV = class
private
FProximoJogadorJogaX: Boolean;
...
end;
function TTabuleiroJV.GetPrimeiroJogadorJogaX: Boolean;
begin
Result := FProximoJogadorJogaX;
end;
procedure TTabuleiroJV.SetPrimeiroJogadorJogaX(const Value: Boolean);
begin
FProximoJogadorJogaX := Value;
end;
Executamos os testes novamente, e tudo correu bem!
Agora, é uma boa hora pra olhar o código e refatorar. Olhando o código, percebemos que no início de cada teste estamos criando a classe TTabuleiroJV. Isto é uma violação do princípio DRY (Don´t Repeat Yourself). Bem, pra resolver este problema podemos mover a criação do objeto para o método SetUp e atribuindo-o a uma variável de instância, evitando, assim as repetidas instanciações.
Veja como ficou:
// *************************************************
// Esta é a classe que efetuará os testes unitários
// *************************************************
type
TTabuleiroJVTests = class(TTestCase)
private
FTabuleiro: TTabuleiroJV;
protected
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestCriarTabuleiro;
procedure TestSetPrimeiroJogador;
procedure TestSetPrimeiroJogadorNovamente;
end;
{ TTabuleiroJVTests }
// *****************************************************************************
// Este método é chamado no início de cada teste
// pode ser usado pra configurar o teste, criar variáveis de instância, etc.
// *****************************************************************************
procedure TTabuleiroJVTests.SetUp;
begin
inherited;
FTabuleiro := TTabuleiroJV.Create;
end;
// *****************************************************************************
// Este método é chamado no fim de cada teste
// pode ser usado pra liberar os recursos, entre outras coisas
// *****************************************************************************
procedure TTabuleiroJVTests.TearDown;
begin
inherited;
FreeAndNil(FTabuleiro);
end;
procedure TTabuleiroJVTests.TestCriarTabuleiro;
begin
CheckNotNull(FTabuleiro);
CheckFalse(FTabuleiro.GameOver);
end;
procedure TTabuleiroJVTests.TestSetPrimeiroJogador;
begin
FTabuleiro.PrimeiroJogadorJogaX := True;
CheckTrue(FTabuleiro.PrimeiroJogadorJogaX);
end;
procedure TTabuleiroJVTests.TestSetPrimeiroJogadorNovamente;
begin
FTabuleiro.PrimeiroJogadorJogaX := True;
CheckTrue(FTabuleiro.PrimeiroJogadorJogaX);
FTabuleiro.PrimeiroJogadorJogaX := False;
CheckFalse(FTabuleiro.PrimeiroJogadorJogaX);
end;
Executamos o nosso teste novamente, para nos certificarmos de não ter quebrado nada durante o refatoramento. Uma vez que tudo ocorreu bem, continuamos.
É hora de atualizar a nossa lista:
4. Configurar o primeiro jogador depois que o jogo é iniciado;
O único teste que ficou na lista é aquele que eu ainda não tenho a menor idéia de como irei testá-lo no momento, visto que o jogo ainda não pode ser iniciado. Por isso, vamos deixá-lo um pouco de lado e retornaremos a ele futuramente, no momento apropriado.
Vamos alistar outros testes:
4. Configurar o primeiro jogador depois que o jogo é iniciado;
5. Marcar a primeira casa;
6. Marcar duas casas em seguida;
7. Marcar três casas em seguida;
8. Colocar uma marca numa casa ocupada;
9. Colocar uma marca numa coluna fora da faixa;
10. Colocar uma marca numa linha fora da faixa;
Vamos escrever o teste #5:
procedure TTabuleiroJVTests.TestMarcarAPrimeiraCasa; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(1, 1); CheckTrue(FTabuleiro.CasaContemUmX(1, 1)); end;
A primeira implementação dos métodos segue abaixo:
function TTabuleiroJV.CasaContemUmX(lin, col: Integer): Boolean; begin Result := False; end; procedure TTabuleiroJV.MarcarCasa(lin, col: Integer); begin end;
Isto resultará na falha do teste #5, visto que ainda não o implementamos completamente. Vamos atingir o verde com o seguinte código:
TTabuleiroJV = class
private
FCasas: array [1..3, 1..3] of string;
public
constructor Create;
procedure MarcarCasa(lin, col: Integer);
function CasaContemUmX(lin, col: Integer): Boolean;
end;
constructor TTabuleiroJV.Create;
begin
FCasas[1,1] := '';
FCasas[1,2] := '';
FCasas[1,3] := '';
FCasas[2,1] := '';
FCasas[2,2] := '';
FCasas[2,3] := '';
FCasas[3,1] := '';
FCasas[3,2] := '';
FCasas[3,3] := '';
end;
procedure TTabuleiroJV.MarcarCasa(lin, col: Integer);
begin
FCasas[lin, col] := 'X';
end;
function TTabuleiroJV.CasaContemUmX(lin, col: Integer): Boolean;
begin
Result := (FCasas[lin, col] = 'X');
end;
Ao executarmos os testes novamente obtemos sucesso em todos os testes. Vamos dar continuidade ao processo.
Em primeiro lugar, vamos atualizar a nossa lista:
4. Configurar o primeiro jogador depois que o jogo é iniciado;
6. Marcar duas casas em seguida;
7. Marcar três casas em seguida;
8. Colocar uma marca numa casa ocupada;
9. Colocar uma marca numa coluna fora da faixa;
10. Colocar uma marca numa linha fora da faixa;
E agora, vamos implementar o teste #6.
procedure TTabuleiroJVTests.TestMarcarDuasCasasEmSeguida; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(1, 1); CheckTrue(FTabuleiro.CasaContemUmX(1, 1)); FTabuleiro.MarcarCasa(1, 2); CheckFalse(FTabuleiro.CasaContemUmX(1, 2)); end;
Este teste irá falhar. Visto que CasaContemUmX(1, 2) irá retornar True, ao passo que esperamos que seja False. Olhando o código percebemos o motivo disto: Não estamos alternando de “X” pra “O” a cada jogada. Portanto, aqui vai a parte do código que corrige esse comportamento errado:
procedure TTabuleiroJV.MarcarCasa(lin, col: Integer);
begin
FCasas[lin, col] := 'O';
if FProximoJogadorJogaX then
begin
FCasas[lin, col] := 'X';
FProximoJogadorJogaX := not FProximoJogadorJogaX;
end;
end;
Ao executarmos os testes novamento obteremos sucesso. Portanto, atualizamos a nossa lista:
Vamos pro teste #7:
procedure TTabuleiroJVTests.TestMarcarTresCasasEmSeguida; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(2, 1); CheckTrue(FTabuleiro.CasaContemUmX(2, 1), '1ª casa'); FTabuleiro.MarcarCasa(2, 2); CheckFalse(FTabuleiro.CasaContemUmX(2, 2), '2ª casa'); FTabuleiro.MarcarCasa(1, 2); CheckTrue(FTabuleiro.CasaContemUmX(1, 2), '3ª casa'); // <- aki="" end="" falha="" pre=""> Poxa! O que foi que deu errado desta vez??? Não estava funcionando tudo direitinho? Por que agora está dando erro? Analisando o método marcar descobrimos o erro:procedure TTabuleiroJV.MarcarCasa(lin, col: Integer); begin FCasas[lin, col] := 'O'; if FProximoJogadorJogaX then begin FCasas[lin, col] := 'X'; FProximoJogadorJogaX := not FProximoJogadorJogaX; end; end;Veja que a alternação de jogadores só funcionada se o proximo jogador for X. Portanto temos que tirar FProximoJogadorJogaX := not FproximoJogadorJogaX de dentro do if:procedure TTabuleiroJV.MarcarCasa(lin, col: Integer); begin FCasas[lin, col] := 'O'; if FProximoJogadorJogaX then FCasas[lin, col] := 'X'; FProximoJogadorJogaX := not FProximoJogadorJogaX; end;Viram como é importante ter uma boa bateria de testes? No primeiro e no segundo teste, parecia estar correto. Mas havia um bug escondidinho lá que só percebemos no terceiro teste. Vamos atualizar a nossa lista:1.Criar Tabuleiro;2.Configurar o primeiro jogador;3.Configurar o primeiro jogador novamente;4. Configurar o primeiro jogador depois que o jogo é iniciado;5.Marcar a primeira casa;6.Marcar duas casas em seguida;7.Marcar três casas em seguida;8. Colocar uma marca numa casa ocupada; 9. Colocar uma marca numa coluna fora da faixa; 10. Colocar uma marca numa linha fora da faixa; Agora é a vez do teste #8. O sucesso deste teste está na falha. Como assim? Bem, ele não deve permitir que eu marque uma casa já ocupada não é verdade? Neste caso ele deve reportar um erro. Portanto, vamos codificar o teste:interface type EJogoDaVelhaError = class(Exception); implementation . . . procedure TTabuleiroJVTests.TestMarcarUmaCasaOcupada; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(2, 1); // Aki dizemos pro framework que estamos aguardando uma exceção do tipo // EJogoDaVelhaError. // Se esta exceção não ocorrer, o teste irá falhar ExpectedException := EJogoDaVelhaError; FTabuleiro.MarcarCasa(2, 1); ExpectedException := nil; end;Ao executarmos o teste veremos que ele realmente falha. Isto acontece porque ainda não implementamos as alterações para verificação desta condição de erro no método MarcarCasa(). Veja como fica:procedure TTabuleiroJV.MarcarCasa(lin, col: Integer); begin if FCasas[lin, col] <> '' then raise EJogoDaVelhaError.Create('A casa já está ocupada'); FCasas[lin, col] := 'O'; if FProximoJogadorJogaX then FCasas[lin, col] := 'X'; FProximoJogadorJogaX := not FProximoJogadorJogaX; end;Vamos atualizar a nossa lista:1.Criar Tabuleiro;2.Configurar o primeiro jogador;3.Configurar o primeiro jogador novamente;4. Configurar o primeiro jogador depois que o jogo é iniciado;5.Marcar a primeira casa;6.Marcar duas casas em seguida;7.Marcar três casas em seguida;8.Colocar uma marca numa casa ocupada;9. Colocar uma marca numa coluna fora da faixa; 10. Colocar uma marca numa linha fora da faixa; Neste momento, eu tenho certeza que você já tem uma boa noção sobre Desenvolvimento Dirigido por testes. Que tal tentar fazer os dois ultimos testes sozinho? No proximo artigo, veremos: 1. Mais testes unitários (para tornar o nosso JogoDaVelha mais robusto); 2. Como implementar a Interface com o usuário. Veja o código final no Anexo I: Referências: Artigo elaborado e adaptado por Marcos George, baseado em um artigo de Venkat Subramaniam (http://www.agiledeveloper.com/articles/TDDPartI.pdf) Anexo I – código fonte de uTabuleiro.pasunit uTabuleiro; interface uses SysUtils, Classes; type EJogoDaVelhaError = class(Exception); TTabuleiroJV = class private FCasas: array [1..3, 1..3] of string; FProximoJogadorJogaX: Boolean; function GetGameOver: Boolean; procedure SetPrimeiroJogadorJogaX(const Value: Boolean); function GetPrimeiroJogadorJogaX: Boolean; public constructor Create; procedure MarcarCasa(lin, col: Integer); function CasaContemUmX(lin, col: Integer): Boolean; property GameOver: Boolean read GetGameOver; property PrimeiroJogadorJogaX: Boolean read GetPrimeiroJogadorJogaX write SetPrimeiroJogadorJogaX; end; implementation uses TestFramework; { TTabuleiroJV } constructor TTabuleiroJV.Create; begin FCasas[1,1] := ''; FCasas[1,2] := ''; FCasas[1,3] := ''; FCasas[2,1] := ''; FCasas[2,2] := ''; FCasas[2,3] := ''; FCasas[3,1] := ''; FCasas[3,2] := ''; FCasas[3,3] := ''; end; function TTabuleiroJV.GetGameOver: Boolean; begin Result := False; end; function TTabuleiroJV.GetPrimeiroJogadorJogaX: Boolean; begin Result := FProximoJogadorJogaX; end; function TTabuleiroJV.CasaContemUmX(lin, col: Integer): Boolean; begin Result := (FCasas[lin, col] = 'X'); end; procedure TTabuleiroJV.MarcarCasa(lin, col: Integer); begin if FCasas[lin, col] <> '' then raise EJogoDaVelhaError.Create('A casa já está ocupada'); FCasas[lin, col] := 'O'; if FProximoJogadorJogaX then FCasas[lin, col] := 'X'; FProximoJogadorJogaX := not FProximoJogadorJogaX; end; procedure TTabuleiroJV.SetPrimeiroJogadorJogaX(const Value: Boolean); begin FProximoJogadorJogaX := Value; end; // ************************************************* // Esta é a classe que efetuará os testes unitários // ************************************************* type TTabuleiroJVTests = class(TTestCase) private FTabuleiro: TTabuleiroJV; protected procedure SetUp; override; procedure TearDown; override; published procedure TestCriarTabuleiro; procedure TestSetPrimeiroJogador; procedure TestSetPrimeiroJogadorNovamente; procedure TestMarcarAPrimeiraCasa; procedure TestMarcarDuasCasasEmSeguida; procedure TestMarcarTresCasasEmSeguida; procedure TestMarcarUmaCasaOcupada; end; { TTabuleiroJVTests } // ***************************************************************************** // Este método é chamado no início de cada teste // pode ser usado pra configurar o teste, criar variáveis de instância, etc. // ***************************************************************************** procedure TTabuleiroJVTests.SetUp; begin inherited; FTabuleiro := TTabuleiroJV.Create; end; // ***************************************************************************** // Este método é chamado no fim de cada teste // pode ser usado pra liberar os recursos, entre outras coisas // ***************************************************************************** procedure TTabuleiroJVTests.TearDown; begin inherited; FreeAndNil(FTabuleiro); end; procedure TTabuleiroJVTests.TestCriarTabuleiro; begin CheckNotNull(FTabuleiro); CheckFalse(FTabuleiro.GameOver); end; procedure TTabuleiroJVTests.TestSetPrimeiroJogador; begin FTabuleiro.PrimeiroJogadorJogaX := True; CheckTrue(FTabuleiro.PrimeiroJogadorJogaX); end; procedure TTabuleiroJVTests.TestSetPrimeiroJogadorNovamente; begin FTabuleiro.PrimeiroJogadorJogaX := True; CheckTrue(FTabuleiro.PrimeiroJogadorJogaX); FTabuleiro.PrimeiroJogadorJogaX := False; CheckFalse(FTabuleiro.PrimeiroJogadorJogaX); end; procedure TTabuleiroJVTests.TestMarcarAPrimeiraCasa; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(1, 1); CheckTrue(FTabuleiro.CasaContemUmX(1, 1)); end; procedure TTabuleiroJVTests.TestMarcarDuasCasasEmSeguida; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(1, 1); CheckTrue(FTabuleiro.CasaContemUmX(1, 1)); FTabuleiro.MarcarCasa(1, 2); CheckFalse(FTabuleiro.CasaContemUmX(1, 2)); end; procedure TTabuleiroJVTests.TestMarcarTresCasasEmSeguida; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(2, 1); CheckTrue(FTabuleiro.CasaContemUmX(2, 1), '1ª casa'); FTabuleiro.MarcarCasa(2, 2); CheckFalse(FTabuleiro.CasaContemUmX(2, 2), '2ª casa'); FTabuleiro.MarcarCasa(1, 2); CheckTrue(FTabuleiro.CasaContemUmX(1, 2), '3ª casa'); end; procedure TTabuleiroJVTests.TestMarcarUmaCasaOcupada; begin FTabuleiro.PrimeiroJogadorJogaX := True; FTabuleiro.MarcarCasa(2, 1); // Aki dizemos pro framework que estamos aguardando uma exceção do tipo // EJogoDaVelhaError. // Se esta exceção não ocorrer, o teste irá falhar ExpectedException := EJogoDaVelhaError; FTabuleiro.MarcarCasa(2, 1); ExpectedException := nil; end; initialization TestFramework.RegisterTest('TTabuleiro Tests', TTabuleiroJVTests.Suite); end.->
Nenhum comentário:
Postar um comentário