Como exatamente os testes de unidade devem ser escritos sem zombar extensivamente?

80

Pelo que entendi, o objetivo dos testes de unidade é testar unidades de código isoladamente . Isso significa que:

  1. Eles não devem quebrar nenhuma alteração de código não relacionada em nenhum outro local da base de código.
  2. Apenas um teste de unidade deve ser interrompido por um bug na unidade testada, em oposição aos testes de integração (que podem ser interrompidos em montões).

Tudo isso implica que toda dependência externa de uma unidade testada deve ser ridicularizada. E quero dizer todas as dependências externas , não apenas as "camadas externas", como rede, sistema de arquivos, banco de dados, etc.

Isso leva a uma conclusão lógica, que praticamente todos os testes de unidade precisam zombar . Por outro lado, uma rápida pesquisa no Google sobre zombaria revela vários artigos que afirmam que "zombar é um cheiro de código" e deve ser evitada na maior parte (embora não completamente).

Agora, para a pergunta (s).

  1. Como os testes de unidade devem ser escritos corretamente?
  2. Onde exatamente está a linha entre eles e os testes de integração?

Atualização 1

Por favor, considere o seguinte pseudo-código:

class Person {
    constructor(calculator) {}

    calculate(a, b) {
        const sum = this.calculator.add(a, b);

        // do some other stuff with the `sum`
    }
}

Um teste que testa o Person.calculatemétodo sem zombar da Calculatordependência (dado que Calculatoré uma classe leve que não acessa "o mundo exterior") pode ser considerado um teste de unidade?

Alexander Lomia
fonte
10
Parte disso é apenas a experiência de design que virá com o tempo. Você aprenderá a estruturar seus componentes para que eles não tenham muitas dependências difíceis de zombar. Isso significa que a testabilidade deve ser uma meta de design secundária de qualquer software. Esse objetivo é muito mais fácil de satisfazer se o teste for escrito antes ou junto com o código, por exemplo, usando TDD e / ou BDD.
amon
33
Coloque testes que são executados de maneira rápida e confiável em uma pasta. Coloque testes lentos e potencialmente frágeis em outro. Execute os testes na primeira pasta o mais rápido possível (literalmente toda vez que você pausa na digitação e o código é compilado é o ideal, mas nem todos os ambientes de desenvolvimento suportam isso). Execute os testes mais lentos com menos frequência (quando você faz uma pausa para o café, por exemplo). Não se preocupe com os nomes de unidade e integração. Ligue rápido e devagar, se quiser. Não importa.
David Arno
6
"que praticamente todos os testes de unidade precisam zombar" Sim, e daí? "uma rápida pesquisa no Google sobre zombeteiro revela toneladas de artigos que afirmam que 'zombaria é um cheiro de código'" eles estão errados .
Michael
13
@ Michael Simplesmente declarar "sim, sim" e declarar a opinião contrária errada não é uma ótima maneira de abordar um assunto controverso como esse. Talvez escreva uma resposta e explique por que você acha que a zombaria deve estar em toda parte, e talvez por que você acha que "toneladas de artigos" são inerentemente erradas?
AJFaraday
6
Como você não citou "zombar é um cheiro de código", posso apenas supor que você está interpretando mal o que lê. Zombar não é um cheiro de código. A necessidade de usar reflexão ou outras travessuras para injetar sua zombaria é um cheiro de código. A dificuldade de zombar é inversamente proporcional à qualidade do seu design de API. Se você pode escrever testes de unidade simples e diretos que apenas passam zombarias para os construtores, você está fazendo certo.
TKK 28/11

Respostas:

59

o objetivo dos testes de unidade é testar unidades de código isoladamente.

Martin Fowler no teste de unidade

O teste de unidade é frequentemente mencionado no desenvolvimento de software e é um termo que eu conheço durante todo o meu tempo escrevendo programas. Como a maioria das terminologias de desenvolvimento de software, no entanto, é muito mal definida, e vejo que a confusão pode ocorrer quando as pessoas pensam que ela é mais definida do que realmente é.

O que Kent Beck escreveu em Test Driven Development, por exemplo

Eu os chamo de "testes de unidade", mas eles não correspondem muito bem à definição aceita de testes de unidade

Qualquer afirmação de "o ponto dos testes de unidade é" dependerá fortemente de qual definição de "teste de unidade" está sendo considerada.

Se sua perspectiva é que seu programa é composto de muitas unidades pequenas que dependem uma da outra, e se você se restringir a um estilo que testa cada unidade isoladamente, muitas duplas de teste são uma conclusão inevitável.

O conselho conflitante que você vê vem de pessoas que operam sob um conjunto diferente de suposições.

Por exemplo, se você estiver escrevendo testes para apoiar os desenvolvedores durante o processo de refatoração, e dividir uma unidade em duas é uma refatoração que deve ser suportada, então algo precisa dar. Talvez esse tipo de teste precise de um nome diferente? Ou talvez precisemos de um entendimento diferente de "unidade".

Você pode comparar:

Um teste que testa o método Person.calculate sem zombar da dependência da Calculadora (dado que a Calculadora é uma classe leve que não acessa "o mundo exterior") pode ser considerado um teste de unidade?

Eu acho que é a pergunta errada a fazer; é novamente uma discussão sobre rótulos , quando acredito que realmente nos importamos com propriedades .

Quando estou introduzindo alterações no código, não me importo com o isolamento de testes - já sei que "o erro" está em algum lugar da minha pilha atual de edições não verificadas. Se eu executar os testes com freqüência, limite a profundidade dessa pilha e encontrar o erro é trivial (no caso extremo, os testes são executados após cada edição - a profundidade máxima da pilha é uma). Mas executar os testes não é o objetivo - é uma interrupção -, portanto, há valor em reduzir o impacto da interrupção. Uma maneira de reduzir a interrupção é garantir que os testes sejam rápidos ( Gary Bernhardt sugere 300ms , mas eu não descobri como fazer isso nas minhas circunstâncias).

Se a chamada Calculator::addnão aumentar significativamente o tempo necessário para executar o teste (ou qualquer uma das outras propriedades importantes para este caso de uso), eu não me incomodaria em usar um teste duplo - ele não fornece benefícios que superam os custos .

Observe as duas suposições aqui: um ser humano como parte da avaliação de custos e a pequena pilha de mudanças não verificadas na avaliação de benefícios. Nas circunstâncias em que essas condições não se mantêm, o valor do "isolamento" muda bastante.

Veja também Hot Lava , de Harry Percival.

VoiceOfUnreason
fonte
5
uma coisa que o mocking add faz é provar que a calculadora pode ser ridicularizada, ou seja, que o design não combina pessoa e calculadora (embora isso também possa ser verificado de outras maneiras)
jk.
40

Como exatamente os testes de unidade devem ser escritos sem zombar extensivamente?

Ao minimizar os efeitos colaterais no seu código.

Tomando seu código de exemplo, se, calculatorpor exemplo, falar com uma API da web, você pode criar testes frágeis que dependem da capacidade de interagir com essa API da web ou criar uma farsa. Se, no entanto, é um conjunto determinístico e livre de estado de funções de cálculo, você não (e não deve) zombar dele. Se o fizer, corre o risco de que o seu mock se comporte de maneira diferente do código real, causando bugs em seus testes.

Zombarias só devem ser necessárias para o código que lê / grava no sistema de arquivos, bancos de dados, pontos de extremidade de URL, etc; dependentes do ambiente em que você está executando; ou que são de natureza altamente estável e não determinística. Portanto, se você mantiver essas partes do código no mínimo e ocultá-las atrás de abstrações, elas serão fáceis de zombar e o restante do código evitará a necessidade de zombarias.

Para os pontos de código que têm efeitos colaterais, vale a pena escrever testes que zombam e testes que não. Estes últimos precisam de cuidados, pois serão inerentemente frágeis e possivelmente lentos. Portanto, convém executá-los, digamos, da noite para o dia em um servidor de IC, e não sempre que você salvar e criar seu código. Os testes anteriores, porém, devem ser executados o mais rápido possível. Se cada teste é um teste de unidade ou integração, torna-se acadêmico e evita "guerras de chamas" sobre o que é e o que não é um teste de unidade.

David Arno
fonte
8
Essa é a resposta correta, tanto na prática quanto em termos de evitar um debate semântico sem sentido.
Jared Smith
Você tem um exemplo de uma base de código de código aberto não trivial que usa esse estilo e ainda obtém uma boa cobertura de teste?
Joeri Sebrechts
4
@JoeriSebrechts cada FP one? Exemplo
Jared Smith
Não é exatamente o que estou procurando, pois é apenas uma coleção de funções que são independentes uma da outra, não um sistema de funções que se chamam. Como você evita ter que construir argumentos complexos para uma função com o objetivo de testá-la, se essa função é uma das de nível superior? Por exemplo, o loop principal de um jogo.
Joeri Sebrechts 28/11/19
1
@JoeriSebrechts hmm, ou estou entendendo mal o que você quer ou não se aprofundou o suficiente no exemplo que dei. As funções ramda usam chamadas internas em todo o lugar em sua fonte (por exemplo R.equals). Como essas funções são, na maioria das vezes, puras, geralmente não são ridicularizadas nos testes.
Jared Smith
31

Essas questões são bem diferentes em suas dificuldades. Vamos fazer a pergunta 2 primeiro.

Testes de unidade e testes de integração são claramente separados. Um teste de unidade testa uma unidade (método ou classe) e usa outras unidades apenas o necessário para atingir esse objetivo. Zombar pode ser necessário, mas não é o objetivo do teste. Um teste de integração testa a interação entre diferentes unidades reais . Essa diferença é toda a razão pela qual precisamos de testes de unidade e de integração - se um fez o trabalho do outro bem o suficiente, não precisaríamos, mas aconteceu que geralmente é mais eficiente usar duas ferramentas especializadas em vez de uma ferramenta generalizada .

Agora, a pergunta importante: como você deve fazer o teste de unidade? Como dito acima, os testes de unidade devem construir estruturas auxiliares apenas na medida do necessário . Muitas vezes, é mais fácil usar um banco de dados falso do que o seu banco de dados real ou mesmo qualquer banco de dados real. No entanto, zombar por si só não tem valor. Se muitas vezes acontece, é mais fácil usar componentes reais de outra camada como entrada para um teste de unidade de nível médio. Nesse caso, não hesite em usá-los.

Muitos profissionais temem que, se o teste de unidade B reutilizar as classes que já foram testadas pelo teste de unidade A, um defeito na unidade A cause falhas de teste em vários locais. Eu considero este não é um problema: um conjunto de testes tem de ter sucesso de 100% , a fim de dar-lhe a tranquilidade que você precisa, por isso não é um grande problema de ter muitas falhas - afinal, você faz tem um defeito. O único problema crítico seria se um defeito desencadeasse poucas falhas.

Portanto, não faça uma religião de zombaria. É um meio, não um fim; portanto, se você puder evitar o esforço extra, deve fazê-lo.

Kilian Foth
fonte
4
The only critical problem would be if a defect triggered too few failures.esse é um dos pontos fracos da zombaria. Temos que "programar" o comportamento esperado para que possamos falhar, fazendo com que nossos testes terminem como "falsos positivos". Mas zombar é uma técnica muito útil para atingir o determinismo (a condição mais importante do teste). Eu os uso em todos os meus projetos, quando possível. Eles também me mostram quando a integração é muito complexa ou a dependência muito estreita.
LAIV
1
Se uma unidade que está sendo testada usa outras unidades, não é realmente um teste de integração? Porque, em essência, esta unidade estaria testando a interação entre as referidas unidades, exatamente como faria um teste de integração.
Alexander Lomia 27/11/2018
11
@AlexanderLomia: Como você chamaria uma unidade? Você chamaria de 'String' uma unidade também? Eu faria, mas não sonharia em zombar disso.
Bart van Ingen Schenau 27/11/19
5
"Os testes de unidade e os testes de integração estão claramente separados. Um teste de unidade testa uma unidade (método ou classe) e usa outras unidades apenas o necessário para atingir esse objetivo ". Aqui está o problema. Essa é a sua definição de teste de unidade. O meu é bem diferente. Portanto, a distinção entre eles é apenas "claramente separada" para qualquer definição, mas a separação varia entre as definições.
David Arno
4
@Voo Tendo trabalhado com essas bases de código, embora possa ser um incômodo encontrar o problema original (especialmente se a arquitetura pintou as coisas que você usaria para depurá-la), ainda tive mais problemas com zombarias que causaram o problema. testes para continuar trabalhando depois que o código que eles foram usados ​​para testar quebrou.
27418 James_pic
6

OK, então responda suas perguntas diretamente:

Como os testes de unidade devem ser escritos corretamente?

Como você diz, você deve estar zombando de dependências e testando apenas a unidade em questão.

Onde exatamente está a linha entre eles e os testes de integração?

Um teste de integração é um teste de unidade em que suas dependências não são zombadas.

Um teste que testa o método Person.calculate sem zombar da Calculadora pode ser considerado um teste de unidade?

Não. Você precisa injetar a dependência da calculadora nesse código e pode escolher entre uma versão simulada ou uma versão real. Se você usar um teste simulado, é um teste de unidade, se você usar um teste real, é um teste de integração.

No entanto, uma ressalva. você realmente se importa com o que as pessoas pensam que seus testes devem ser chamados?

Mas sua verdadeira pergunta parece ser a seguinte:

uma pesquisa rápida no Google sobre zombaria revela vários artigos que afirmam que "zombar é um cheiro de código" e deve ser evitada na maior parte (embora não completamente).

Acho que o problema aqui é que muitas pessoas usam zombarias para recriar completamente as dependências. Por exemplo, eu posso zombar da calculadora no seu exemplo como

public class MockCalc : ICalculator
{
     public Add(int a, int b) { return 4; }
}

Eu não faria algo como:

myMock = Mock<ICalculator>().Add((a,b) => {return a + b;})
myPerson.Calculate()
Assert.WasCalled(myMock.Add());

Eu diria que isso seria "testar meu mock" ou "testar a implementação". Eu diria " Não escreva Mocks! * Assim".

Outras pessoas discordariam de mim, iniciaríamos enormes guerras de chamas em nossos blogs sobre o melhor caminho para zombar, o que realmente não faria sentido a menos que você entendesse todo o contexto das várias abordagens e realmente não ofereça muito valor para alguém que só quer escrever bons testes.

Ewan
fonte
Obrigado por uma resposta exaustiva. Quanto a me preocupar com o que as outras pessoas pensam sobre meus testes - na verdade, eu quero evitar escrever testes de semi-integração e semi-unidade que tendem a se tornar uma bagunça pouco confiável à medida que o projeto avança.
Alexander Lomia 27/11
sem problemas, acho que o problema é que as definições das duas coisas não são 100% acordadas por todos.
Ewan
Gostaria de mudar o nome da classe MockCalca StubCalc, e chamá-lo um esboço não um mock. martinfowler.com/articles/…
bdsl
@bdsl Esse artigo tem 15 anos
Ewan
4
  1. Como os testes de unidade devem ser implementados corretamente?

Minha regra geral é que os testes de unidade adequados:

  • São codificados contra interfaces, não implementações . Isso tem muitos benefícios. Por um lado, garante que suas classes sigam o Princípio de Inversão de Dependência do SOLID . Além disso, é isso que suas outras classes fazem ( certo? ), Portanto seus testes devem fazer o mesmo. Além disso, isso permite testar várias implementações da mesma interface enquanto reutiliza grande parte do código de teste (apenas a inicialização e algumas asserções mudam).
  • São independentes . Como você disse, alterações em qualquer código externo não podem afetar o resultado do teste. Como tal, os testes de unidade podem ser executados no momento da construção. Isso significa que você precisa de zombarias para remover quaisquer efeitos colaterais. No entanto, se você estiver seguindo o Princípio da inversão de dependência, isso deve ser relativamente fácil. Boas estruturas de teste como Spock podem ser usadas para fornecer dinamicamente implementações simuladas de qualquer interface para uso em seus testes com codificação mínima. Isso significa que cada classe de teste precisa apenas exercitar o código de exatamente uma classe de implementação, mais a estrutura de teste (e talvez as classes de modelo ["beans"]).
  • Não exija um aplicativo em execução separado . Se o teste precisar "conversar com algo", seja um banco de dados ou um serviço da Web, é um teste de integração, não um teste de unidade. Eu traço a linha nas conexões de rede ou no sistema de arquivos. Um banco de dados SQLite puramente na memória, por exemplo, é um jogo justo, na minha opinião, para um teste de unidade, se você realmente precisar.

Se houver classes de utilitário de estruturas que complicam o teste de unidade, você pode até achar útil criar interfaces e classes "wrapper" muito simples para facilitar a zombaria dessas dependências. Esses invólucros não seriam necessariamente sujeitos a testes de unidade.

  1. Onde está exatamente a linha entre eles [testes de unidade] e testes de integração?

Eu achei essa distinção a mais útil:

  • Os testes de unidade simulam o "código do usuário" , verificando o comportamento das classes de implementação em relação ao comportamento e semântica desejados das interfaces no nível do código .
  • Os testes de integração simulam o usuário , verificando o comportamento do aplicativo em execução em relação aos casos de uso especificados e / ou APIs formais. Para um serviço da web, o "usuário" seria um aplicativo cliente.

Há uma área cinza aqui. Por exemplo, se você pode executar um aplicativo em um contêiner do Docker e executar os testes de integração como o estágio final de uma compilação e destruir o contêiner posteriormente, é aceitável incluir esses testes como "testes de unidade"? Se este é o seu debate ardente, você está em um bom lugar.

  1. É verdade que praticamente todos os testes de unidade precisam zombar?

Não. Alguns casos de teste individuais serão para condições de erro, como passar nullcomo parâmetro e verificar se você recebe uma exceção. Muitos testes como esse não exigem zombaria. Além disso, implementações que não têm efeitos colaterais, como processamento de sequência ou funções matemáticas, podem não exigir zombarias, porque você simplesmente verifica a saída. Mas acho que a maioria das classes exige, pelo menos, uma simulação em algum lugar do código de teste. (Quanto menos, melhor.)

O problema "cheiro de código" que você mencionou surge quando você tem uma classe muito complicada, que requer uma longa lista de dependências simuladas para escrever seus testes. Essa é uma pista de que você precisa refatorar a implementação e dividir as coisas, para que cada classe tenha uma área menor e uma responsabilidade mais clara e, portanto, seja mais facilmente testável. Isso melhorará a qualidade a longo prazo.

Apenas um teste de unidade deve ser interrompido por um erro na unidade testada.

Não acho que seja uma expectativa razoável, porque funciona contra a reutilização. Você pode ter um privatemétodo, por exemplo, chamado por vários publicmétodos publicados por sua interface. Um bug introduzido nesse método pode causar várias falhas de teste. Isso não significa que você deve copiar o mesmo código em cada publicmétodo.

amora
fonte
3
  1. Eles não devem quebrar nenhuma alteração de código não relacionada em nenhum outro local da base de código.

Não tenho muita certeza de como essa regra é útil. Se uma mudança em uma classe / método / qualquer coisa que possa quebrar o comportamento de outra no código de produção, as coisas são, na realidade, colaboradores e não relacionadas. Se seus testes forem interrompidos e o código de produção não, eles serão suspeitos.

  1. Apenas um teste de unidade deve ser interrompido por um bug na unidade testada, em oposição aos testes de integração (que podem ser interrompidos em montões).

Eu consideraria essa regra com suspeita também. Se você é realmente bom o suficiente para estruturar seu código e escrever seus testes de modo que um erro cause exatamente uma falha no teste de unidade, então você está dizendo que já identificou todos os possíveis erros, mesmo que a base de código evolua para casos de uso que você não antecipou.

Onde exatamente está a linha entre eles e os testes de integração?

Não acho que seja uma distinção importante. O que é uma 'unidade' de código de qualquer maneira?

Tente encontrar pontos de entrada nos quais você possa escrever testes que apenas 'façam sentido' em termos do domínio do problema / regras de negócios com as quais esse nível de código está lidando. Frequentemente, esses testes são de natureza um tanto "funcional" - inserem uma entrada e testam se a saída é a esperada. Se os testes expressam o comportamento desejado do sistema, eles geralmente permanecem bastante estáveis, mesmo quando o código de produção evolui e é refatorado.

Como exatamente os testes de unidade devem ser escritos sem zombar extensivamente?

Não leia muito sobre a palavra 'unidade' e incline-se a usar suas classes de produção reais em testes, sem se preocupar muito se estiver envolvendo mais de uma delas em um teste. Se um deles é difícil de usar (porque é preciso muita inicialização ou precisa atingir um banco de dados / servidor de e-mail real etc.), deixe seus pensamentos se tornarem zombadores / falsos.

topo morto
fonte
" O que é uma 'unidade' de código de qualquer maneira? " Pergunta muito boa que pode ter respostas inesperadas que podem até depender de quem está respondendo. Normalmente, a maioria das definições de testes de unidade as explica como relacionadas a um método ou a uma classe, mas essa não é uma medida realmente útil de uma "unidade" em todos os casos. Se eu tenho um Person:tellStory()método que incorpora os detalhes de uma pessoa em uma string, então retorna isso, então a "história" é provavelmente uma unidade. Se eu criar um método auxiliar particular que retire parte do código, não acredito que tenha introduzido uma nova unidade - não preciso testá-lo separadamente.
VLAZ #
2

Primeiro, algumas definições:

Um teste de unidade testa as unidades isoladamente de outras unidades, mas o que isso significa não é definido concretamente por nenhuma fonte autorizada, portanto, vamos defini-lo um pouco melhor: se os limites de E / S forem cruzados (se a E / S é rede, disco, tela ou entrada da interface do usuário), há um local semi-objetivo para desenhar uma linha. Se o código depender de E / S, ele estará cruzando o limite de uma unidade e, portanto, precisará zombar da unidade responsável por essa E / S.

Sob essa definição, não vejo uma razão convincente para zombar de coisas como funções puras, o que significa que o teste de unidade se presta a funções puras, ou funções sem efeitos colaterais.

Se você deseja unidades de teste de unidade com efeitos, as unidades responsáveis ​​pelos efeitos devem ser ridicularizadas, mas talvez você deva considerar um teste de integração. Portanto, a resposta curta é: "se você precisar zombar, pergunte a si mesmo se o que você realmente precisa é de um teste de integração". Mas há uma resposta melhor e mais longa aqui, e a toca do coelho é muito mais profunda. Zombar pode ser o meu cheiro de código favorito, porque há muito a aprender com eles.

Code Smells

Para isso, veremos a Wikipedia:

Na programação de computadores, um cheiro de código é qualquer característica no código-fonte de um programa que possivelmente indica um problema mais profundo.

Continua mais tarde ...

"Cheiros são certas estruturas no código que indicam violação dos princípios fundamentais do design e afetam negativamente a qualidade do design". Suryanarayana, Girish (novembro de 2014). Refatoração para cheiros de design de software. Morgan Kaufmann. p. 258

Cheiros de código geralmente não são erros; eles não são tecnicamente incorretos e não impedem o funcionamento do programa. Em vez disso, indicam fraquezas no design que podem retardar o desenvolvimento ou aumentar o risco de bugs ou falhas no futuro.

Em outras palavras, nem todos os odores de código são ruins. Em vez disso, são indicações comuns de que algo pode não ser expresso em sua forma ideal e o cheiro pode indicar uma oportunidade para melhorar o código em questão.

No caso de zombaria, o cheiro indica que as unidades que parecem estar pedindo zombarias dependem das unidades a serem zombadas. Pode ser uma indicação de que não decompusemos o problema em partes atomicamente solucionáveis ​​e que podem indicar uma falha de design no software.

A essência de todo o desenvolvimento de software é o processo de decompor um grande problema em partes menores e independentes (decomposição) e compor as soluções para formar um aplicativo que resolve o grande problema (composição).

A zombaria é necessária quando as unidades usadas para dividir o grande problema em partes menores dependem uma da outra. Dito de outra maneira, a zombaria é necessária quando nossas supostas unidades atômicas de composição não são realmente atômicas e nossa estratégia de decomposição falhou em decompor o problema maior em problemas menores e independentes a serem resolvidos.

O que faz com que zombar de um código cheira não é que exista algo inerentemente errado com zombaria - às vezes é muito útil. O que faz parecer um código é que ele pode indicar uma fonte problemática de acoplamento em seu aplicativo. Às vezes, remover essa fonte de acoplamento é muito mais produtivo do que escrever uma farsa.

Existem muitos tipos de acoplamentos, e alguns são melhores que outros. Entender que as zombarias são um cheiro de código pode ensiná-lo a identificar e evitar os piores tipos no início do ciclo de vida do design do aplicativo, antes que o cheiro se torne algo pior.

Eric Elliott
fonte
1

A zombaria só deve ser usada como último recurso, mesmo em testes de unidade.

Um método não é uma unidade, e mesmo uma classe não é uma unidade. Uma unidade é qualquer separação lógica de código que faz sentido, independentemente do que você chama. Um elemento importante de ter código bem testado é poder refatorar livremente, e parte de ser capaz de refatorar livremente significa que você não precisa alterar seus testes para fazer isso. Quanto mais você zomba, mais precisa alterar seus testes quando refatorar. Se você considerar o método a unidade, precisará alterar seus testes sempre que refatorar. E se você considerar a classe a unidade, precisará alterar seus testes toda vez que quiser dividir uma classe em várias classes. Quando você precisa refatorar seus testes para refatorar seu código, as pessoas optam por não refatorar o código, o que é a pior coisa que pode acontecer com um projeto. É essencial que você possa dividir uma classe em várias classes sem ter que refatorar seus testes, ou você terminará com aulas de espaguete de 500 linhas de tamanho grande. Se você está tratando métodos ou classes como suas unidades com teste de unidade, provavelmente não está fazendo Programação Orientada a Objetos, mas algum tipo de programação funcional mutante com objetos.

Isolar seu código para um teste de unidade não significa que você zomba de tudo que está fora dele. Se assim fosse, você teria que zombar da aula de matemática do seu idioma, e absolutamente ninguém acha que é uma boa ideia. Dependências internas não devem ser tratadas de maneira diferente das dependências externas. Você confia que eles são bem testados e funcionam como deveriam. A única diferença real é que, se suas dependências internas estão quebrando seus módulos, você pode parar o que está fazendo para corrigi-lo, em vez de precisar publicar um problema no GitHub e cavar em uma base de código que você não entende para corrigi-lo ou espero o melhor.

Isolar seu código significa apenas que você trata suas dependências internas como caixas-pretas e não testa as coisas que estão acontecendo dentro delas. Se você possui o Módulo B, que aceita entradas de 1, 2 ou 3, e o Módulo A, que o chama, você não tem seus testes para o Módulo A para cada uma dessas opções, basta escolher uma e usá-la. Isso significa que seus testes para o Módulo A devem testar as diferentes maneiras de tratar as respostas do Módulo B, não as coisas que você passa para ele.

Portanto, se o seu controlador passa um objeto complexo para uma dependência, e essa dependência faz várias coisas possíveis, talvez o salve no banco de dados e talvez retorne uma variedade de erros, mas tudo o que o seu controlador realmente faz é simplesmente verificar se ele retorna um erro ou não e repassar essas informações, tudo o que você testa em seu controlador é um teste para se ele retornar um erro e repassá-lo e um teste para se não retornar um erro. Você não testa se algo foi salvo no banco de dados ou que tipo de erro é o erro, porque isso seria um teste de integração. Você não precisa zombar da dependência para fazer isso. Você isolou o código.

TiggerToo
fonte