O Desenvolvimento Orientado por Testes (TDD) é um processo de desenvolvimento de software introduzido por Kent Beck como parte da metodologia de programação extrema. No TDD, os testes unitários são escritos primeiro antes de quaisquer implementações serem desenvolvidas.
Os requisitos são capturados como casos de teste. Como novos requisitos são acrescentados ao software, o mesmo acontece com os novos casos de teste. À medida que o sistema é desenvolvido, construímos uma imagem maior dos requisitos e aumentamos a cobertura dos testes unitários.
O processo recomendado pela TDD é o seguinte:
Um ponto-chave aqui é que os testes são adicionados primeiro antes de qualquer desenvolvimento ser feito para implementar a funcionalidade. Escrever a solução mais simples permite que o teste seja aprovado, depois isto pode ser refinado por refatoração. Os testes então garantem que qualquer refatoração não tenha introduzido nenhum defeito.
O desenvolvimento orientado por testes geralmente produz códigos de melhor qualidade e reduz a margem para defeitos. Ele encoraja o uso de pequenas iterações ao desenvolver software.
Um efeito colateral útil é que os testes servem como uma forma de documentação para as exigências do software.
O código desenvolvido sob TDD pode levar a um código flexível e extensível. Isto porque os desenvolvedores muitas vezes têm que pensar em termos de unidades menores que podem ser testadas isoladamente e integradas em conjunto. Como apenas a quantidade mínima de código é adicionada para que os testes sejam aprovados, é mais provável que os próprios testes cubram todos os caminhos do código no software.
Quando usado em conjunto com um sistema de controle de fonte, em caso de falha nos testes, o código pode ser revertido para a última versão que teve um conjunto completo de testes que passaram.
Embora seja fácil de usar o TDD com novos projetos, é difícil de aplicar aos sistemas existentes ou herdados. Se um novo código for adicionado ao software antigo, então claramente estas novas partes podem ser desenvolvidas com TDD, mas adicionar testes ao código antigo pode ser difícil e demorado.
Alguns processos são difíceis de testar, tais como processos de Interface de Usuário ou conectividade com banco de dados.
Um grande número de testes de aprovação pode oferecer uma falsa sensação de segurança, levando a testes insuficientes de outras atividades.
Desenvolver e manter uma grande quantidade de testes pode ser um processo demorado.
O desenvolvimento orientado a testes pode ser facilmente realizado usando DUnitX, a estrutura de teste da unidade que é fornecida com Delphi. Usando o assistente integrado no Delphi, é fácil criar um projeto de teste DUnitX, completo com uma unidade de teste e alguns testes de amostra.
Escrever um teste de falha pode ser tão simples quanto escrever um teste vazio que não chama nenhuma funcionalidade:
unit Tests.VATCalculator; interface uses DUnitX.TestFramework; type [TestFixture] TTestVATCalculator = class public [Test] [TestCase ('100_Pounds', '100,20')] procedure TestGetVATAmount(const NetAmount: Currency; const ExpectedAmount: Currency); end; implementation procedure TTestVATCalculator.TestGetVATAmount(const NetAmount: Currency; const ExpectedAmount: Currency); begin var ReturnValue: Currency; // No implementation Assert.AreEqual(ExpectedAmount, ReturnValue); end; initialization TDUnitX.RegisterTestFixture(TTestVATCalculator); end.
A execução deste teste resultará na falha do teste. Implementar a funcionalidade para o teste com o código mais simples possível:
unit VAT.Calculator.Interfaces; interface type IVATCalculator = interface ['{1576AC66-A116-47EB-9C6A-23D4E2C21B2C}'] function GetVAT(const NetAmount: Currency): Currency; end; implementation end.
unit VAT.Calculator; interface uses VAT.Calculator.Interfaces; type TVATCalculator = class(TInterfacedObject, IVATCalculator) public function GetVAT(const NetAmount: Currency): Currency; end; implementation function TVATCalculator.GetVAT(const NetAmount: Currency): Currency; begin Result := NetAmount * 20 / 100; end; end.
unit Tests.VATCalculator; interface uses DUnitX.TestFramework, VAT.Calculator.Interfaces; type [TestFixture] TTestVATCalculator = class private FVATCalculator: IVATCalculator; public [Setup] procedure Setup; [Test] [TestCase ('100_Pounds', '100,20')] procedure TestGetVATAmount(const NetAmount: Currency; const ExpectedAmount: Currency); end; implementation uses VAT.Calculator; procedure TTestVATCalculator.Setup; begin FVATCalculator := TVATCalculator.Create; end; procedure TTestVATCalculator.TestGetVATAmount(const NetAmount: Currency; const ExpectedAmount: Currency); var ReturnValue: Currency; begin ReturnValue := FVATCalculator.GetVAT(NetAmount); Assert.AreEqual(ExpectedAmount, ReturnValue); end; initialization TDUnitX.RegisterTestFixture(TTestVATCalculator); end.
Os testes agora serão aprovados e o processo pode ser continuado para acrescentar mais requisitos e refatorar conforme necessário.
O desenvolvimento orientado a testes é uma ferramenta poderosa para capturar os requisitos e garantir que os testes sejam desenvolvidos ao mesmo tempo que o software. É melhor utilizado para novos projetos ou para adicionar novos módulos a projetos existentes. Embora possa ser demorado, os testes fornecem um mecanismo valioso para garantir que qualquer nova adição ao software não quebre nenhuma funcionalidade existente. Ele continua sendo uma ferramenta muito útil na caixa de ferramentas de um desenvolvedor de software.
Contato