Usando Assertions

<< Click to Display Table of Contents >>

Navigation:  Novatos > Mais Dicas Programação Delphi >

Usando Assertions

Previous pageReturn to chapter overviewNext page

Assertions são validações sobre os fundamentos da lógica de um programa. A verificação de um assertion é a validação de que uma condição necessária para o algoritimo funcionar, algo que você supõe ser verdadeiro, é realmente válido durante a execução do programa.

Em Delphi, a sintaxe de um Assertion é a seguinte:
Assert(Condition: Boolean; [Message: string]);

 

Isso quer dizer que Assert é um procedimento que aceita uma condição, e opcionalmente uma mensagem de erro. É usado como uma prática defensiva de codificação para garantir o estado do seu programa, pegando falhas antes que aconteçam. Se a condição passada para o assert for avaliada como falso, será gerada uma exceção com a mensagem informada (ou ‘assertion failure’, se não for passada nenhuma), e o mais interessante, por uma mágica de compilador a mensagem também inclui o nome do arquivo e a linha onde a falha ocorreu.

 

Assertions não são feitas para validação de entrada de dados ou de resultados de funções, mas sim de pressupostos que deveriam sempre ser verdadeiros, e se não forem, indicam existe uma bug em alguma parte do seu programa. Por esse motivo, é prática comum desabilitar a validação de assertions no executável final produzido para distribuição, o que pode ser feito através de Project/Options, Compiler, Debug Information, Assertions.

Tendo em mente tudo isso, vamos a alguns exemplos:
procedure TMinhaClasse.AdicionaItem(Item: TMeuItem);

begin

  Assert(Assigned(Item), 'Foi passado um item nil!'); // ERRADO!

  FList.Add(Item);

end;

 

Um Assertion Failure não é uma validação apropriada para a validação de parâmetros de métodos públicos. Enquanto seria aceitável se esse método fosse chamado apenas internamente, se ele for disponibilizado para outros desenvolvedores ou para ser utilizado em outras classes, ele deve funcionar corretamente em todos os casos, com assertions habilitadas ou não. Desta forma, poderiamos escrever
procedure TMinhaClasse.AdicionaItem(Item: TMeuItem);

begin

  if not Assigned(Item) then

    raise Exception.Create('Foi passado um item nil!');

  Assert(FList.Add(Item) >= 0'Item não foi adicionado corretamente'); //ERRADO!

end;

 

Esta segunda tentativa de utilizar o assert também é falha, pois coloca código importante dentro do assert. Se essa classe for compilada com assertions desligados, esse código não vai ser executado e a lógica não vai mais funcionar.
procedure TMinhaClasse.AdicionaItem(Item: TMeuItem);

begin

  if not Assigned(Item) then

    raise Exception.Create('Foi passado um item nil!');

  Assert(Assigned(FLista), "FLista é nil');

  FList.Add(Item);

end;

 

Neste último exemplo estamos fazendo uma validação adequada para um assert - FLista deve estar sempre atribuída durante o tempo de vida da classe. Se não tiver, é um problema da lógica interna da classe (e não um problema externo como a validação do parâmetro). Essa validação apontaria o erro adequadamente durante a depuração, evitando causar um Access Violation mais dificil se se diagnosticar. E, caso seja removida para o executável de produção, não vai causar nenhum comportamento diferente no programa.