O que você faz quando está escrevendo um teste e chega ao ponto em que precisa fazer o teste passar e percebe que precisa de uma parte adicional da funcionalidade que deve ser separada em sua própria função? Essa nova função também precisa ser testada, mas o ciclo do TDD diz: Faça um teste falhar, faça-o passar e depois refatorar. Se estou na etapa em que estou tentando fazer meu teste passar, não devo sair e iniciar outro teste com falha para testar a nova funcionalidade que preciso implementar.
Por exemplo, estou escrevendo uma classe de ponto que tem uma função WillCollideWith ( LineSegment ) :
public class Point {
// Point data and constructor ...
public bool CollidesWithLine(LineSegment lineSegment) {
Vector PointEndOfMovement = new Vector(Position.X + Velocity.X,
Position.Y + Velocity.Y);
LineSegment pointPath = new LineSegment(Position, PointEndOfMovement);
if (lineSegment.Intersects(pointPath)) return true;
return false;
}
}
Eu estava escrevendo um teste para CollidesWithLine quando percebi que precisaria de uma função LineSegment.Intersects ( LineSegment ) . Mas devo parar o que estou fazendo no meu ciclo de teste para criar essa nova funcionalidade? Isso parece quebrar o princípio "Vermelho, Verde, Refatorador".
Devo apenas escrever o código que detecta esse lineSegments Intersect dentro da função CollidesWithLine e refatorá- lo depois que ele estiver funcionando? Isso funcionaria neste caso, pois eu posso acessar os dados do LineSegment , mas e nos casos em que esse tipo de dados é privado?
fonte
No ciclo TDD:
Na fase "faça o teste passar", você deve escrever a implementação mais simples que fará o teste passar . Para fazer seu teste passar, você decidiu criar um novo colaborador para lidar com a lógica ausente, porque talvez tenha sido muito trabalho colocar em sua classe de ponto para fazer seu teste passar. É aí que reside o problema. Suponho que o teste que você estava tentando passar foi um grande passo . Portanto, acho que o problema está no seu teste, você deve excluir / comentar esse teste e descobrir um teste mais simples que permita dar um pequeno passo sem introduzir a parte LineSegment.Intersects (LineSegment). Se você passar no teste, poderá refatorarseu código (aqui você aplicará o princípio SRP) movendo essa nova lógica para um método LineSegment.Intersects (LineSegment). Seus testes ainda serão aprovados porque você não alterou nenhum comportamento, mas apenas mudou alguns códigos.
Na sua solução de design atual
Mas para mim, você tem um problema de design mais profundo aqui: está violando o Princípio de Responsabilidade Única . O papel de um Ponto é .... ser um ponto, é tudo. Não há inteligência em ser um ponto, é apenas um valor de xey. Pontos são tipos de valor . É o mesmo para segmentos, segmentos são tipos de valor compostos por dois pontos. Eles podem conter um pouco de "inteligência", por exemplo, para calcular seu comprimento com base na posição dos pontos. Mas é isso.
Agora, decidir se um ponto e um segmento estão colidindo é uma responsabilidade por si só. E certamente é muito trabalho para um ponto ou segmento lidar sozinho. Ele não pode pertencer à classe Point, porque, caso contrário, o Points saberá sobre os segmentos. E não pode pertencer a Segmentos porque os segmentos já têm a responsabilidade de cuidar dos pontos dentro do segmento e talvez também calcular a extensão do próprio segmento.
Portanto, essa responsabilidade deve ser de propriedade de outra classe, como por exemplo um "PointSegmentCollisionDetector", que teria um método como:
bool AreInCollision (ponto p, segmentos)
E isso é algo que você testaria separadamente a partir de pontos e segmentos.
O bom desse design é que agora você pode ter uma implementação diferente do seu detector de colisão. Portanto, seria fácil, por exemplo, avaliar o seu mecanismo de jogo (presumo que você esteja escrevendo um jogo: p) alternando seu método de detecção de colisão em tempo de execução. Ou fazer algumas verificações / experimentos visuais em tempo de execução entre diferentes estratégias de detecção de colisões.
No momento, ao colocar essa lógica em sua classe point, você está bloqueando as coisas e pressionando muita responsabilidade na classe Point.
Espero que faça sentido,
fonte
A coisa mais fácil de se fazer no estilo TDD seria extrair uma interface para o LineSegment e alterar o parâmetro do método para incluir a interface. Em seguida, você pode simular o segmento de linha de entrada e codificar / testar o método Intersect independentemente.
fonte
Com o jUnit4, você pode usar a
@Ignore
anotação para os testes que deseja adiar.Adicione a anotação a cada método que você deseja adiar e continue escrevendo testes para a funcionalidade necessária. Volte para refatorar os casos de teste mais antigos posteriormente.
fonte