Em sua palestra TDD, onde tudo deu errado , Ian Cooper enfatiza a intenção original de Kent Beck por trás dos testes de unidade no TDD (para testar comportamentos, não métodos de classes especificamente) e argumenta para evitar o acoplamento dos testes à implementação.
No caso de comportamento como save X to some data source
em um sistema com um conjunto típico de serviços e repositórios, como podemos testar a unidade para salvar alguns dados no nível de serviço, através do repositório, sem acoplar o teste aos detalhes da implementação (como chamar um método específico )? Evitar esse tipo de acoplamento realmente não vale o esforço / mal, de alguma forma?
unit-testing
tdd
coupling
Andy Hunt
fonte
fonte
Respostas:
Seu exemplo específico é um caso em que você geralmente precisa testar verificando se um determinado método foi chamado, porque
saving X to data source
significa se comunicar com uma dependência externa ; portanto, o comportamento que você deve testar é que a comunicação está ocorrendo conforme o esperado .No entanto, isso não é uma coisa ruim. As interfaces de fronteira entre seu aplicativo e suas dependências externas não são detalhes de implementação ; na verdade, elas são definidas na arquitetura do seu sistema; o que significa que é provável que esse limite não seja alterado (ou, se necessário, seria o tipo de alteração menos frequente). Portanto, acoplar seus testes a uma
repository
interface não deve causar muitos problemas (se houver, considere se a interface não está roubando responsabilidades do aplicativo).Agora, considere apenas as regras de negócios de um aplicativo, dissociadas da interface do usuário, bancos de dados e outros serviços externos. É aqui que você deve estar livre para alterar a estrutura e o comportamento do código. É aqui que os testes de acoplamento e os detalhes da implementação o forçam a alterar mais código de teste que código de produção, mesmo quando não há alteração no comportamento geral do aplicativo. É aqui que testar em
State
vez deInteraction
nos ajudar a ir mais rápido.PS: Não é minha intenção dizer se o teste por Estado ou Interações é a única maneira verdadeira de TDD - acredito que seja uma questão de usar a ferramenta certa para o trabalho certo.
fonte
Minha interpretação dessa palestra é:
Não está indicado na conversa, mas acho que o contexto assumido para o conselho é algo como:
Portanto, testar um componente é o maior escopo possível no qual algo ainda pode ser chamado razoavelmente de teste de unidade. Isso é bem diferente de como algumas pessoas, especialmente acadêmicos, usam o termo. Não é nada parecido com os exemplos no tutorial típico da ferramenta de teste de unidade. No entanto, corresponde à sua origem nos testes de hardware; placas e módulos são testados em unidade, não fios e parafusos. Ou pelo menos você não constrói uma Boeing simulada para testar um parafuso ...
Extrapolando disso, e jogando em alguns dos meus próprios pensamentos,
Se você fizer isso de maneira adequada e limpa, mal precisará de uma ferramenta de zombaria; ele é usado apenas algumas vezes por sistema.
Um banco de dados geralmente é um colaborador, portanto, é falsificado em vez de ridicularizado. Isso seria doloroso de implementar manualmente; felizmente, essas coisas já existem .
O padrão de teste básico é executar algumas seqüências de operações (por exemplo, salvar e recarregar um documento); confirme que funciona. É o mesmo que para qualquer outro cenário de teste; nenhuma alteração (de trabalho) na implementação provavelmente fará com que esse teste falhe.
A exceção é onde os registros do banco de dados são gravados, mas nunca lidos pelo sistema em teste; por exemplo, registros de auditoria ou similares. Essas são saídas e, portanto, devem ser ridicularizadas. O padrão de teste é fazer alguma sequência de operações; confirme se a interface de auditoria foi chamada com métodos e argumentos, conforme especificado.
Observe que, mesmo aqui, desde que você esteja usando uma ferramenta de simulação de segurança de tipo como mockito , renomear um método de interface não pode causar uma falha no teste. Se você usar um IDE com os testes carregados, ele será refatorado junto com o método renomear. Caso contrário, o teste não será compilado.
fonte
Minha sugestão é usar uma abordagem de teste baseada em estado:
Dado Temos o banco de dados de teste em um estado conhecido
QUANDO O serviço é chamado com argumentos X
ENTÃO Afirme que o banco de dados mudou de seu estado original para o estado esperado chamando métodos de repositório somente leitura e verificando seus valores retornados
Dessa maneira, você não depende de nenhum algoritmo interno do serviço e pode refatorar sua implementação sem precisar alterar os testes.
O único acoplamento aqui é a chamada do método de serviço e as chamadas do repositório necessárias para ler os dados do banco de dados, o que é bom.
fonte