O que são as escolas de TDD em Londres e Chicago?

88

Eu tenho ouvido falar sobre o estilo de Londres versus o estilo de Chicago (às vezes chamado de estilo Detroit) do Test Driven Development (TDD).

Workshop do grupo de usuários de programação extrema de Utah:

O TDD no estilo de interação também é chamado de estilo mockista , ou estilo de Londres, depois do clube Extreme Tuesday de Londres, onde se tornou popular. Geralmente é contrastado com o TDD clássico ou no estilo Detroit, que é mais baseado no estado.

Oficina de Jason Gorman :

O workshop abrange a escola de TDD de Chicago (teste de comportamento e triangulação com base no estado) e a escola de Londres , que se concentra mais em testes de interação, zombaria e TDD de ponta a ponta, com ênfase especial no design orientado pela responsabilidade e no A abordagem Tell, Don't Ask do OO foi re-popularizada recentemente pelo excelente livro Guiding By Tests, de Growing Software Orientado a Objetos, de Steve Freeman e Nat Pryce .

O post TDD clássico ou "London School"? por Jason Gorman foi útil, mas seus exemplos me confundiram, porque ele usa dois exemplos diferentes em vez de um exemplo com as duas abordagens. Quais são as diferenças? Quando você usa cada estilo?

Arturo Herrero
fonte

Respostas:

76

Suponha que você tenha uma classe chamada "razão", um método chamado "calcular" que usa uma "Calculadora" para fazer diferentes tipos de cálculos, dependendo dos argumentos passados ​​para "calcular", por exemplo "multiplicar (x, y)" ou "subtrair ( x, y) ".

Agora, suponha que você queira testar o que acontece quando você chama ledger.calculate ("5 * 7").

A escola de Londres / Interação solicitaria que você indicasse se o Calculator.multiply (5,7) foi chamado. As várias estruturas de simulação são úteis para isso, e podem ser muito úteis se, por exemplo, você não possuir o objeto "Calculadora" (suponha que seja um componente ou serviço externo que não possa testar diretamente, mas sim sabe que você tem que ligar de uma maneira particular).

A escola de Chicago / State faria com que você declarasse se o resultado é 35. As estruturas jUnit / nUnit geralmente são voltadas para isso.

Ambos são testes válidos e importantes.

Matthew Flynn
fonte
Muito bom exemplo.
sevenseacat
1
Vou adicionar mais alguns motivos para usar cada um deles: se o importante é determinar se algo mudou ou não com base na ação que está sendo executada (por exemplo, o ledger.bucket.value se torna 35 quando ledger.calculate ("5 * 7 ") é chamado), você deseja usar asserções de estado (escola de Chicago). Isso é mais útil quando você tem controle total sobre o estado do sistema antes que o método seja chamado e quando realmente controla o que o método faz.
Matthew Flynn
1
Se o importante é saber que um segundo método é chamado (por exemplo, Calculator.multiply (5, 7)), você deseja usar asserções de atividade, como através de um objeto simulado. Isso é mais útil se o método chamado tiver um efeito colateral desejado (por exemplo, salvar dados, incrementar um contador, enviar uma mensagem etc.) ou se você realmente não controlar o que o método faz, para que o valor de retorno seja inconsistente . Além disso, se você não pode controlar facilmente o estado do sistema, o melhor que pode fazer é determinar quais atividades ocorrem.
Matthew Flynn
A abordagem de Londres é útil quando a classe Calculator é potencialmente demorada por algum motivo, ou envolve uma rede e, portanto, pode ser confusa nas configurações de dev / qa. Ou seja, a zombaria permite que seus testes sejam rápidos e confiáveis ​​nos casos em que não seria possível de outra forma.
21711 Kevin
1
A abordagem de Londres também argumenta para fornecer sinais de feedback mais claros, porque, se Calculatorregressar multiply, você verá dois testes falharem: o teste do razão e o teste da calculadora, mas apenas um teste falhará se você zombar da calculadora. Isso pode facilitar a identificação da origem do bug, principalmente se o sistema for complexo.
Matthias
30

O artigo Mocks Arn't Stubs , de Martin Fowler, é uma boa introdução ao tópico.

Dependendo do estilo de design escolhido (e dos princípios de design nos quais você cria seus programas), há pelo menos duas maneiras de ver um objeto:

  1. Como uma unidade que executa cálculos com base em entradas. Como resultado dessa computação, o objeto pode retornar um valor ou alterar seu estado.
  2. Como um elemento ativo que se comunica com outros elementos no sistema por passagem de mensagem.

No primeiro caso, você está interessado no que sai do processamento ou em qual estado o objeto é deixado após esse processamento. É aqui que métodos como assertEquals()entrar na imagem. Nesse caso, não importa muito quais outros objetos estavam envolvidos no processamento, quais métodos foram chamados etc. Esse tipo de verificação é chamado verificação baseada em estado e é o estilo "clássico".

No segundo caso, como a maioria dos objetos nem retorna nenhum resultado (por exemplo, voidmétodos em Java), você está mais interessado em saber como os objetos se comunicam e se eles passam as mensagens corretas nas circunstâncias impostas pelo teste. Essas interações geralmente são verificadas com o auxílio de estruturas simuladas. Esse tipo de verificação é chamado de verificação baseada em comportamento ou em interação. Uma de suas implicações é a técnica chamada Behavior Driven Development, pela qual você desenvolve uma classe assumindo que seus colaboradores já existem (mesmo que ainda não existam), para que você possa codificar em suas interfaces.

Observe que essa não é uma opção de escolha. Você pode ter um estilo de design que combine as duas abordagens para obter o melhor de cada uma.

Otavio Macedo
fonte