Devo testar métodos privados ou apenas públicos? [fechadas]


348

Eu li este post sobre como testar métodos privados. Geralmente não os testo, porque sempre achei mais rápido testar apenas métodos públicos que serão chamados de fora do objeto. Você testa métodos particulares? Devo sempre testá-los?



"Devo testar ajudantes particulares?" Sim. "Devo testar ajudantes particulares diretamente?" Depende, geralmente, se você pode testá-los facilmente através da interface pública, por que testá-los diretamente? Se se torna complexo testar todos os aspectos dos auxiliares por meio de uma interface pública, o componente sobreviveu à sua existência como uma única unidade?
Mihai Danila

Respostas:


328

Eu não teste métodos unitários privados. Um método privado é um detalhe de implementação que deve estar oculto para os usuários da classe. Testar métodos privados interrompe o encapsulamento.

Se eu achar que o método privado é enorme ou complexo ou importante o suficiente para exigir seus próprios testes, basta colocá-lo em outra classe e torná-lo público lá ( Object Object ). Então, posso testar facilmente o método anteriormente privado, mas agora público, que agora vive em sua própria classe.


88
Discordo. Idealmente, você escreve um teste rápido antes de começar a codificar uma função. Pense na entrada típica e qual será a saída. Escreva o teste (que não deve demorar mais que alguns segundos) e codifique até que ele faça o teste corretamente. Não há razão para abandonar esse estilo de trabalho para métodos privados.
Frank

254
Dizer que métodos privados não precisam de testes é como dizer que um carro é bom desde que funcione bem, e não importa o que está por trás. Mas não seria bom saber que alguns cabos internos estão começando a se soltar - mesmo que o usuário não perceba nada? Claro, você pode tornar tudo público, mas qual é o objetivo? Você sempre desejará alguns métodos particulares.
Frank

37
"Um método privado é um detalhe de implementação que deve estar oculto para os usuários da classe." mas os testes estão realmente do mesmo lado da interface da classe que os usuários "regulares" (tempo de execução)? ;)
mlvljr

34
O perigo de extrair qualquer coisa que você queira testar para outra classe é que você pode acabar sobrecarregando a engenharia em excesso do seu produto e tendo um milhão de componentes reutilizáveis ​​que nunca são reutilizados.
Occulus

44
Comparar um pedaço de código com um carro está errado; código não " vai mal " com o tempo, é eterno . Se o teste da interface pública for apenas até determinar que " parece bom ", o teste do código público será insuficiente. Nesse caso, testar métodos particulares separadamente não fará o teste geral completo, por mais que você tente. Concentre-se em testar exaustivamente seu código público como um todo, usando o conhecimento do funcionamento interno do código para criar os cenários certos.
Rustyx

293

Qual é o objetivo dos testes?

A maioria das respostas até agora está dizendo que métodos privados são detalhes de implementação que não importam (ou pelo menos não deveriam), desde que a interface pública esteja bem testada e funcionando. Isso é absolutamente correto se seu único objetivo para testar é garantir que a interface pública funcione .

Pessoalmente, meu principal uso para testes de código é garantir que futuras alterações de código não causem problemas e ajudar meus esforços de depuração, se o fizerem. Acho que testar os métodos privados da mesma maneira que a interface pública (se não mais!) Promove esse objetivo.

Considere: Você tem o método público A, que chama o método privado B. A e B usam o método C. C é alterado (talvez por você, talvez por um fornecedor), fazendo com que A comece a falhar em seus testes. Não seria útil fazer testes para B também, mesmo que seja privado, para que você saiba se o problema está no uso de A por C, no uso de C por B ou em ambos?

Testar métodos particulares também agrega valor nos casos em que a cobertura de teste da interface pública está incompleta. Embora essa seja uma situação que geralmente queremos evitar, o teste da unidade de eficiência depende tanto dos testes de detecção de bugs quanto dos custos associados de desenvolvimento e manutenção desses testes. Em alguns casos, os benefícios da cobertura de teste de 100% podem ser considerados insuficientes para garantir os custos desses testes, produzindo lacunas na cobertura de teste da interface pública. Nesses casos, um teste bem direcionado de um método privado pode ser uma adição muito eficaz à base de código.


72
O problema aqui é que essas "alterações futuras no código" invariavelmente significam refatorar o funcionamento interno de alguma classe. Isso acontece com tanta frequência que escrever testes cria uma barreira à refatoração.
Outlaw Programmer

40
Além disso, se você estiver alterando continuamente seus testes de unidade, perderá toda a consistência e provavelmente criará bugs nos próprios testes de unidade.
17 de 26

6
@ 17 Se os testes e a implementação forem modificados de forma síncrona (como parece. Deve ser), haverá muito menos problemas.
Mlvljr 21/05

11
@Sauronlord, a razão pela qual você testa métodos privados é porque, se você testar apenas os métodos públicos, quando o teste falhar, não sabemos diretamente onde está a causa raiz da falha. Pode estar em um testDoSomething()ou em testDoSomethingPrivate(). Isso torna o teste menos valioso. . Aqui está mais razões para testes privados stackoverflow.com/questions/34571/... :
Pacerier

3
@Pacerier Também há uma diferença entre testar seu código e ter um processo de teste automatizado contínuo. Obviamente, você deve garantir que seu método privado funcione, mas não deve ter testes acoplando-o ao método privado, porque não faz parte do caso de uso do software.
Didier A.

150

Costumo seguir os conselhos de Dave Thomas e Andy Hunt em seu livro Pragmatic Unit Testing :

Em geral, você não deseja quebrar nenhum encapsulamento para testar (ou, como mamãe costumava dizer, "não exponha suas partes íntimas!"). Na maioria das vezes, você deve poder testar uma classe exercitando seus métodos públicos. Se houver uma funcionalidade significativa oculta por trás de acesso privado ou protegido, isso pode ser um sinal de aviso de que há outra classe lá lutando para sair.

Mas, às vezes, não consigo parar de testar métodos privados, porque isso me dá a sensação de que estou construindo um programa completamente robusto.


9
Eu recomendaria desabilitar os testes de unidade direcionados a métodos particulares. Eles são um acoplamento de código e sobrecarregam o trabalho futuro de refatoração, ou mesmo às vezes atrapalham a adição ou modificação de recursos. É bom escrever um teste para eles enquanto você os implementa, como uma maneira automatizada de afirmar que sua implementação funciona, mas não é benéfico manter os testes como regressão.
Didier A.

61

Sinto-me compelido a testar funções privadas, pois estou seguindo cada vez mais uma de nossas recomendações mais recentes de controle de qualidade em nosso projeto:

Não mais que 10 em complexidade ciclomática por função.

Agora, o efeito colateral da aplicação dessa política é que muitas das minhas funções públicas muito grandes se dividem em muitas funções privadas mais focadas e melhor nomeadas .
A função pública ainda está lá (é claro), mas é essencialmente reduzida a chamada de todas essas subfunções privadas

Isso é realmente legal, porque o callstack agora é muito mais fácil de ler (em vez de um bug em uma função grande, eu tenho um bug em uma sub-subfunção com o nome das funções anteriores no callstack para me ajudar a entender 'como cheguei lá')

No entanto, agora parece mais fácil testar por unidade diretamente essas funções privadas e deixar o teste da grande função pública para algum tipo de teste de 'integração', em que um cenário precisa ser tratado.

Apenas meus 2 centavos.


2
para reagir ao @jop, não sinto a necessidade de exportar essas funções privadas (criadas devido à divisão de uma grande função pública complexa e ciclomática) para outra classe. Eu gosto de tê-los ainda fortemente acoplados à função pública, na mesma classe. Mas ainda testado em unidade.
VonC 19/09/08

2
Minha experiência é que esses métodos privados são apenas métodos utilitários que estão sendo reutilizados por esses métodos públicos. Às vezes, é mais conveniente dividir a classe original em duas (ou três) classes mais coesas, tornando esses métodos privados públicos em suas próprias classes e, portanto, testáveis.
19138 jop

7
não, no meu caso, essas novas funções privadas são realmente parte do algoritmo maior representado pela função pública. Essa função é dividida em partes menores, que não são úteis, mas as etapas de um processo maior. Daí a necessidade de unidade-teste-los (em vez de unidade-teste toda a algo de uma vez)
VonC

Para aqueles interessados ​​em complexidade ciclomática, adicionei uma pergunta sobre o tópico: stackoverflow.com/questions/105852/…
VonC

Ops, o URL da pergunta foi alterado devido a um erro de digitação no título! stackoverflow.com/questions/105852/…
VonC 19/09/08

51

Sim, eu testo funções privadas, porque, embora sejam testadas pelos seus métodos públicos, é bom no TDD (Test Driven Design) testar a menor parte do aplicativo. Mas as funções privadas não são acessíveis quando você está na classe da unidade de teste. Aqui está o que fazemos para testar nossos métodos particulares.

Por que temos métodos particulares?

Funções privadas existem principalmente em nossa classe porque queremos criar código legível em nossos métodos públicos. Não queremos que o usuário desta classe chame esses métodos diretamente, mas através de nossos métodos públicos. Além disso, não queremos alterar seu comportamento ao estender a classe (no caso de protegido), portanto, é um privado.

Quando codificamos, usamos o design controlado por teste (TDD). Isso significa que, às vezes, encontramos uma funcionalidade que é privada e deseja testar. Funções privadas não são testáveis ​​no phpUnit, porque não podemos acessá-las na classe Test (elas são privadas).

Achamos que aqui estão três soluções:

1. Você pode testar suas partes privadas através de seus métodos públicos

Vantagens

  • Teste simples de unidade (não são necessários "hacks")

Desvantagens

  • O programador precisa entender o método público, enquanto ele quer apenas testar o método privado
  • Você não está testando a menor parte testável do aplicativo

2. Se o privado é tão importante, talvez seja um código para criar uma nova classe separada para ele

Vantagens

  • Você pode refatorar isso para uma nova classe, porque se for tão importante, outras classes também poderão precisar
  • A unidade testável agora é um método público, portanto testável

Desvantagens

  • Você não deseja criar uma classe se ela não for necessária e usada apenas pela classe de onde o método é proveniente
  • Perda potencial de desempenho devido à sobrecarga adicional

3. Altere o modificador de acesso para (final) protegido

Vantagens

  • Você está testando a menor parte testável do aplicativo. Ao usar a proteção final, a função não será substituída (como uma privada)
  • Sem perda de desempenho
  • Nenhuma sobrecarga extra

Desvantagens

  • Você está alterando um acesso privado para protegido, o que significa que é acessível por seus filhos
  • Você ainda precisa de uma classe Mock na sua classe de teste para usá-la

Exemplo

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

Portanto, nossa unidade de teste agora pode chamar test_sleepWithSuspect para testar nossa antiga função privada.


eddy147, eu realmente gosto do conceito de testar métodos protegidos via zombaria. Obrigado!!!!
Theodore R. Smith

15
Eu só quero salientar que na descrição original do TDD, no teste de unidade, a unidade é a classe , não um método / função. Portanto, quando você menciona "testando a menor parte do aplicativo", é errado referir a menor parte testável como um método. Se você usar essa lógica, poderá estar falando de uma única linha de código em vez de um bloco de código inteiro.
Matt Quigley

@Matt Uma unidade de trabalho pode apontar para uma classe, mas também para um único método.
eddy147

4
@ eddy147 O teste de unidade vem com o Test Driven Development, onde a unidade foi definida como uma classe. Como acontece com o The Internets, a semântica se expandiu para significar muitas coisas (por exemplo, pergunte a 2 pessoas qual é a diferença entre teste de unidade e integração e você receberá 7 respostas). O TDD foi concebido como uma maneira de escrever software com os princípios do SOLID, incluindo Responsabilidade Única, em que uma classe tinha uma única responsabilidade e não deveria ter uma alta complexidade cíclica. No TDD, você escreve sua classe e testa juntos a unidade. Métodos particulares são encapsulados não têm um teste de unidade correspondente.
Matt Quigley

"Quando codificamos, usamos o design controlado por teste (TDD). Isso significa que, às vezes, encontramos uma funcionalidade que é privada e que deseja testar". Não concordo totalmente com esta afirmação. Consulte a minha resposta abaixo para obter mais detalhes. TDD não significa que você é forçado a testar métodos privados. Você pode optar por testar métodos particulares: e essa é a sua escolha, mas não é o TDD que está fazendo você fazer isso.
9788208305168Código do anúncio

41

Não gosto de testar a funcionalidade privada por alguns motivos. Eles são os seguintes (estes são os principais pontos para o pessoal do TLDR):

  1. Normalmente, quando você é tentado a testar o método privado de uma classe, é um cheiro de design.
  2. Você pode testá-los através da interface pública (que é como você deseja testá-los, porque é assim que o cliente os chama / usa). Você pode obter uma falsa sensação de segurança vendo a luz verde em todos os testes aprovados nos seus métodos particulares. É muito melhor / mais seguro testar casos extremos em suas funções privadas por meio de sua interface pública.
  3. Você corre o risco de duplicação severa de testes (testes que parecem muito similares) testando métodos privados. Isso tem grandes conseqüências quando os requisitos mudam, pois mais testes do que o necessário serão interrompidos. Ele também pode colocá-lo em uma posição em que é difícil refatorar por causa do seu conjunto de testes ... o que é a ironia final, porque o conjunto de testes existe para ajudá-lo a redesenhar e refatorar com segurança!

Vou explicar cada uma delas com um exemplo concreto. Acontece que 2) e 3) estão um pouco intricadamente conectados, então o exemplo deles é semelhante, embora eu os considere razões separadas por que você não deve testar métodos privados.

Há momentos em que o teste de métodos particulares é apropriado; é importante estar ciente das desvantagens listadas acima. Vou falar sobre isso mais detalhadamente mais tarde.

Também discuto por que o TDD não é uma desculpa válida para testar métodos privados no final.

Refatorando sua maneira de sair de um design ruim

Um dos (anti) paternos mais comuns que eu vejo é o que Michael Feathers chama de classe "Iceberg" (se você não sabe quem é Michael Feathers, compre / leia seu livro "Trabalhando Efetivamente com o Código Legado". uma pessoa que vale a pena conhecer se você é um engenheiro / desenvolvedor profissional de software). Existem outros (anti) padrões que causam esse problema, mas esse é de longe o mais comum que eu já deparei. As classes "Iceberg" têm um método público e o restante é privado (é por isso que é tentador testar os métodos privados). Isso é chamado de classe "Iceberg" porque geralmente existe um método público solitário, mas o restante da funcionalidade fica oculto sob a forma de métodos particulares.

Avaliador de regras

Por exemplo, você pode querer testar GetNextToken()chamando-a em uma sequência sucessivamente e vendo que ela retorna o resultado esperado. Uma função como essa merece um teste: esse comportamento não é trivial, especialmente se suas regras de tokenização forem complexas. Vamos fingir que não é tão complexo assim, e nós apenas queremos colocar tokens delimitados pelo espaço. Então, você escreve um teste, talvez seja algo assim (algum código psuedo independente da linguagem, espero que a idéia seja clara):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

Bem, isso realmente parece muito bom. Queremos ter certeza de manter esse comportamento ao fazer alterações. Mas GetNextToken()é uma função privada ! Portanto, não podemos testá-lo assim, porque ele nem compila (supondo que estamos usando alguma linguagem que realmente imponha público / privado, ao contrário de algumas linguagens de script como Python). Mas e quanto a mudar de RuleEvaluatorclasse para seguir o Princípio de Responsabilidade Única (Princípio de Responsabilidade Única)? Por exemplo, parece que temos um analisador, tokenizador e avaliador presos em uma classe. Não seria melhor apenas separar essas responsabilidades? Além disso, se você criar uma Tokenizerclasse, seus métodos públicos seriam HasMoreTokens()e GetNextTokens(). A RuleEvaluatorturma poderia ter umTokenizerobjeto como um membro. Agora, podemos manter o mesmo teste acima, exceto que estamos testando a Tokenizerclasse em vez da RuleEvaluatorclasse.

Veja como isso pode ser na UML:

Avaliador de regra refatorado

Observe que esse novo design aumenta a modularidade; portanto, você pode potencialmente reutilizar essas classes em outras partes do seu sistema (antes disso, os métodos privados não são reutilizáveis ​​por definição). Essa é a principal vantagem de desmembrar o RuleEvaluator, juntamente com um maior entendimento / localidade.

O teste seria extremamente semelhante, exceto que, na verdade, seria compilado dessa vez, já que o GetNextToken()método agora é público na Tokenizerclasse:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

Testando componentes particulares por meio de uma interface pública e evitando a duplicação de testes

Mesmo que você não consiga dividir seu problema em menos componentes modulares (que você pode 95% do tempo se tentar fazê-lo), basta testar as funções privadas por meio de uma interface pública. Muitas vezes, os membros privados não valem a pena testar porque serão testados através da interface pública. Muitas vezes o que vejo são testes muito parecidos, mas testam duas funções / métodos diferentes. O que acaba acontecendo é que, quando os requisitos mudam (e sempre mudam), agora você tem 2 testes quebrados em vez de 1. E se você realmente testou todos os seus métodos particulares, pode ter mais 10 testes quebrados em vez de 1. Em resumo , testando funções privadas (usandoFRIEND_TESTou torná-los públicos ou usando reflexão) que poderiam ser testados por meio de uma interface pública podem causar duplicação de teste . Você realmente não quer isso, porque nada machuca mais do que sua suíte de testes que o atrasa. É suposto diminuir o tempo de desenvolvimento e os custos de manutenção! Se você testar métodos particulares que são testados por meio de uma interface pública, o conjunto de testes pode muito bem fazer o oposto e aumentar ativamente os custos de manutenção e o tempo de desenvolvimento. Quando você torna pública uma função privada, ou se usa algo como FRIEND_TESTe / ou reflexão, geralmente acaba se arrependendo a longo prazo.

Considere a seguinte implementação possível da Tokenizerclasse:

insira a descrição da imagem aqui

Digamos que SplitUpByDelimiter()seja responsável por retornar uma matriz de modo que cada elemento na matriz seja um token. Além disso, digamos que GetNextToken()é simplesmente um iterador sobre esse vetor. Portanto, seu teste público pode parecer assim:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

Vamos fingir que temos o que Michael Feather chama de ferramenta tateando . Esta é uma ferramenta que permite tocar em partes íntimas de outras pessoas. Um exemplo é FRIEND_TESTde googletest, ou reflexão, se o idioma suportar.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

Bem, agora digamos que os requisitos mudem e a tokenização se torne muito mais complexa. Você decide que um simples delimitador de string não será suficiente e precisa de uma Delimiterclasse para lidar com o trabalho. Naturalmente, você espera que um teste seja interrompido, mas essa dor aumenta quando você testa funções privadas.

Quando o teste de métodos privados pode ser apropriado?

Não há "tamanho único" no software. Às vezes, é bom (e realmente ideal) "quebrar as regras". Eu defendo fortemente não testar a funcionalidade privada quando puder. Existem duas situações principais em que acho que está tudo bem:

  1. Eu trabalhei extensivamente com sistemas legados (e é por isso que sou um grande fã de Michael Feathers), e posso dizer com segurança que às vezes é simplesmente mais seguro apenas testar a funcionalidade privada. Pode ser especialmente útil para obter "testes de caracterização" na linha de base.

  2. Você está com pressa e precisa fazer o mais rápido possível por aqui e agora. A longo prazo, você não deseja testar métodos privados. Mas vou dizer que geralmente leva algum tempo para refatorar para resolver problemas de design. E às vezes você tem que enviar em uma semana. Tudo bem: faça o que for mais rápido e sujo e teste os métodos particulares usando uma ferramenta de busca, se é o que você acha que é a maneira mais rápida e confiável de realizar o trabalho. Mas entenda que o que você fez foi abaixo do ideal a longo prazo e considere voltar a ele (ou, se foi esquecido, mas você vê mais tarde, conserte).

Provavelmente há outras situações em que está tudo bem. Se você acha que está tudo bem e tem uma boa justificativa, faça-o. Ninguém o está impedindo. Esteja ciente dos custos potenciais.

A desculpa TDD

Como um aparte, eu realmente não gosto de pessoas que usam TDD como uma desculpa para testar métodos privados. Eu pratico TDD e não acho que o TDD o force a fazer isso. Você pode escrever seu teste (para sua interface pública) primeiro e depois escrever o código para satisfazer essa interface. Às vezes, escrevo um teste para uma interface pública e o satisfazi escrevendo um ou dois métodos privados menores também (mas não testo os métodos privados diretamente, mas sei que eles funcionam ou que meu teste público falharia ) Se eu precisar testar casos extremos desse método privado, escreverei um monte de testes que os atingirão através da minha interface pública.Se você não consegue descobrir como atingir os casos extremos, esse é um sinal forte de que você precisa refatorar em pequenos componentes, cada um com seus próprios métodos públicos. É um sinal de que as funções privadas estão fazendo muito e fora do escopo da classe .

Além disso, às vezes, acho que escrevo um teste muito grande para mastigar no momento, e então penso: "eh, voltarei a esse teste mais tarde quando tiver mais API para trabalhar" (eu vou comentar e manter isso no fundo da minha mente). É aqui que muitos desenvolvedores que conheci começarão a escrever testes para suas funcionalidades particulares, usando o TDD como bode expiatório. Eles dizem "ah, bem, eu preciso de outro teste, mas para escrever esse teste, precisarei desses métodos particulares. Portanto, como não consigo escrever nenhum código de produção sem escrever um teste, preciso escrever um teste para um método privado ". Mas o que eles realmente precisam fazer é refatorar em componentes menores e reutilizáveis, em vez de adicionar / testar vários métodos particulares à sua classe atual.

Nota:

Respondi a uma pergunta semelhante sobre o teste de métodos particulares usando o GoogleTest há pouco tempo. Modifiquei principalmente essa resposta para ser mais independente de idioma aqui.

PS Aqui está a palestra relevante sobre aulas de iceberg e ferramentas de tatear de Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU


O problema que tenho ao listar "você pode potencialmente reutilizar essas classes em outras partes do seu sistema" como uma vantagem é que, às vezes, o motivo pelo qual marquei uma função é privada é porque não quero que ela seja usada por outras partes do sistema. o sistema. Este é um problema específico da linguagem: idealmente, isso seria privado para um "módulo", mas se a linguagem não suportar isso (por exemplo, PHP), minha classe representa o módulo, não a unidade: os métodos privados são códigos reutilizáveis com seus próprios contratos, mas só devem ser reutilizados nessa classe.
IMSoP

Entendo o que você está dizendo, mas gosto da maneira como a comunidade Python lida com esse problema. Se você nomear o membro "privado" em questão como líder _, ele indicará "ei, isso é 'privado'. Você pode usá-lo, mas a divulgação completa, ele não foi projetado para reutilização e deve ser usado apenas se você realmente saber o que você está fazendo ". Você pode adotar a mesma abordagem em qualquer idioma: torne esses membros públicos, mas marque-os com uma liderança _. Ou talvez essas funções realmente devam ser privadas e testadas por meio de uma interface pública (consulte a resposta para obter mais detalhes). É de caso a caso, nenhuma regra geral
Matt Messersmith

26

Eu acho que é melhor apenas testar a interface pública de um objeto. Do ponto de vista do mundo exterior, apenas o comportamento da interface pública é importante e é para isso que seus testes de unidade devem ser direcionados.

Depois de escrever alguns testes de unidade sólidos para um objeto, você não precisa voltar e alterar esses testes apenas porque a implementação por trás da interface mudou. Nessa situação, você arruinou a consistência do seu teste de unidade.


21

Se seu método privado não for testado chamando seus métodos públicos, o que está fazendo? Eu estou falando privado não protegido ou amigo.


3
Obrigado. Este é um comentário surpreendentemente subestimado e especialmente relevante, mesmo depois de quase 8 anos desde que foi escrito.
Sauronlord 4/15/15

11
Com o mesmo raciocínio, alguém poderia argumentar para testar apenas o software a partir da interface do usuário (teste no nível do sistema), porque de alguma forma todas as funções no software seriam executadas de alguma forma a partir daí.
Dirk Herrmann

18

Se o método privado estiver bem definido (ou seja, tiver uma função que pode ser testada e não deve mudar ao longo do tempo), então sim. Testo tudo o que é testável, onde faz sentido.

Por exemplo, uma biblioteca de criptografia pode ocultar o fato de executar criptografia de bloco com um método privado que criptografa apenas 8 bytes por vez. Eu escreveria um teste de unidade para isso - ele não deve mudar, mesmo estando oculto, e se quebrar (devido a aprimoramentos futuros de desempenho, por exemplo), quero saber que é a função privada que quebrou, não apenas que uma das funções públicas quebrou.

Acelera a depuração mais tarde.

-Adão


11
Nesse caso, não faria sentido mover esse método privado para outra classe e torná-lo público ou estático?
Outlaw Programmer

+1 Se você não testar suas funções de membro privadas e seu teste da interface pública falhar, tudo o que obterá é um resultado equivalente a algo que está quebrado, sem a menor idéia do que é isso.
Olumide

12

Se você estiver desenvolvendo test-driven (TDD), testará seus métodos particulares.



4
Não é verdade, você testa seus métodos públicos e, uma vez que os testes passam, extrai o código dos métodos públicos em métodos privados durante a etapa "limpeza". Testar métodos privados é uma péssima idéia, porque dificulta a alteração da implementação (e se algum dia você quiser mudar a maneira como faz alguma coisa, poderá alterá-lo e executar todos os seus testes e se sua nova maneira de fazer o está correto, eles deveriam passar, eu não gostaria de mudar todos os meus testes particulares para isso).
Tesseract

11
@Tesseract, se eu pudesse votar de novo em seu comentário mais de uma vez. "... você deve poder alterá-lo e executar todos os seus testes e, se sua nova maneira de fazer as coisas estiver correta, eles devem passar" Esse é um dos principais benefícios dos testes de unidade. Eles permitem refatorar com confiança. Você pode alterar completamente o funcionamento interno privado de sua classe e (sem reescrever todos os seus testes de unidade) ter certeza de que você não quebrou nada, porque todos os seus testes de unidade (existentes) (na sua interface pública) ainda passam.
Lee

Discordo, ver minha resposta abaixo
Matt Messersmith

11

Não sou especialista neste campo, mas o teste de unidade deve testar o comportamento, não a implementação. Os métodos privados são estritamente parte da implementação, portanto o IMHO não deve ser testado.


Onde a implementação é testada? Se alguma funcionalidade usa armazenamento em cache, isso é um detalhe da implementação e o armazenamento em cache não é testado?
Dirk Herrmann

11

Testamos métodos privados por inferência, ou seja, procuramos uma cobertura total de testes de classe de pelo menos 95%, mas apenas nossos testes são chamados a métodos públicos ou internos. Para obter a cobertura, precisamos fazer várias chamadas para o público / interno com base nos diferentes cenários que podem ocorrer. Isso torna nossos testes mais atentos ao objetivo do código que eles estão testando.

A resposta de Trumpi à postagem que você vinculou é a melhor.


9

Acredito que testes de unidade são para testar métodos públicos. Seus métodos públicos usam seus métodos privados; portanto, indiretamente, eles também estão sendo testados.


7

Eu estive discutindo sobre esse assunto por um tempo, especialmente tentando minha mão no TDD.

Eu me deparei com dois posts que acho que abordam esse problema completamente o suficiente no caso do TDD.

  1. Testando métodos particulares, TDD e refatoração orientada a testes
  2. O desenvolvimento orientado a testes não está testando

Em suma:

  • Ao usar técnicas de desenvolvimento orientadas a teste (design), métodos privados devem surgir apenas durante o processo de refatoração de código já em funcionamento e testado.

  • Pela própria natureza do processo, qualquer funcionalidade simples de implementação extraída de uma função completamente testada será auto-testada (ou seja, cobertura de teste indireta).

Para mim, parece claro o suficiente que, na parte inicial da codificação, a maioria dos métodos terá funções de nível superior porque elas estão encapsulando / descrevendo o design.

Portanto, esses métodos serão públicos e testá-los será bastante fácil.

Os métodos privados virão mais tarde, quando tudo estiver funcionando bem e estamos considerando a questão da legibilidade e limpeza .


6

Como citado acima, "Se você não testar seus métodos particulares, como você sabe que eles não quebrarão?"

Esta é uma questão importante. Um dos grandes pontos dos testes de unidade é saber onde, quando e como algo quebrou o mais rápido possível. Diminuindo assim uma quantidade significativa de esforço de desenvolvimento e controle de qualidade. Se tudo o que for testado for público, você não terá cobertura e delineamento honestos dos internos da turma.

Eu descobri que uma das melhores maneiras de fazer isso é simplesmente adicionar a referência de teste ao projeto e colocar os testes em uma classe paralela aos métodos privados. Coloque a lógica de construção apropriada para que os testes não sejam incorporados ao projeto final.

Então você tem todos os benefícios de ter esses métodos testados e pode encontrar problemas em segundos, em vez de minutos ou horas.

Então, em resumo, sim, teste de unidade seus métodos particulares.


2
Discordo. "Se você não testar seus métodos particulares, como você sabe que eles não vão quebrar?" : Eu sei disso porque se meus métodos particulares forem quebrados, os testes que testam meus métodos públicos que dependem desses métodos particulares falharão. Não quero mudar meus testes toda vez que mudo de idéia sobre como implementar os métodos públicos. Também acho que o principal interesse dos testes de unidade não é saber especificamente qual linha de código está com defeito, mas permite que você fique mais ou menos confiante de que não quebrou nada ao fazer alterações (nos métodos particulares).
usar o seguinte

6

Você não deveria . Se seus métodos particulares tiverem complexidade suficiente que deve ser testada, você deverá colocá-los em outra classe. Mantenha alta coesão , uma classe deve ter apenas um propósito. A interface pública da classe deve ser suficiente.


3

Se você não testar seus métodos particulares, como você sabe que eles não vão quebrar?


19
Escrevendo através de testes de seus métodos públicos.
scubabbl 19/09/08

3
Esses métodos privados são supostamente chamados pelos métodos públicos da classe. Portanto, basta testar os métodos públicos que chamam os métodos privados.
jop

11
Se seus métodos públicos estão funcionando corretamente, obviamente os métodos privados que eles acessam estão funcionando corretamente.
17 de 26

Se os testes de seus métodos públicos falharem, você saberá instantaneamente que algo não está correto em um nível inferior no seu objeto / componente / etc.
Rob

3
É muito bom, no entanto, saber que é uma função interna e não apenas as funções externas que quebraram (ou, inversamente, que as funções internas estão bem e que você pode se concentrar na externa).
Adam Davis

2

Obviamente, depende da linguagem. No passado, com c ++, eu declarei a classe testing como uma classe friend. Infelizmente, isso exige que seu código de produção conheça a classe de teste.


5
A palavra-chave friend me deixa triste.
Rob

Isso não é um problema se a classe de teste for implementada em outro projeto. O importante é que o código de produção não faça referência à classe de teste.
Olumide

2

Entendo o ponto de vista em que métodos privados são considerados como detalhes de implementações e não precisam ser testados. E eu continuaria com essa regra se tivéssemos que desenvolver apenas fora do objeto. Mas nós, somos algum tipo de desenvolvedor restrito que está desenvolvendo apenas fora dos objetos, chamando apenas seus métodos públicos? Ou na verdade também estamos desenvolvendo esse objeto? Como não estamos programados para objetos externos, provavelmente precisaremos chamar esses métodos privados para novos métodos públicos que estamos desenvolvendo. Não seria ótimo saber que o método privado resiste a todas as probabilidades?

Eu sei que algumas pessoas podem responder que, se estamos desenvolvendo outro método público para esse objeto, este deve ser testado e é isso (o método privado pode continuar vivendo sem teste). Mas isso também é verdade para qualquer método público de um objeto: ao desenvolver um aplicativo Web, todos os métodos públicos de um objeto são chamados de métodos de controladores e, portanto, podem ser considerados como detalhes de implementação de controladores.

Então, por que estamos testando objetos de unidade? Porque é realmente difícil, para não dizer impossível, ter certeza de que estamos testando os métodos dos controladores com a entrada apropriada que acionará todas as ramificações do código subjacente. Em outras palavras, quanto mais alto estivermos na pilha, mais difícil será testar todo o comportamento. E assim é o mesmo para métodos privados.

Para mim, a fronteira entre métodos privados e públicos é um critério psicológico quando se trata de testes. Os critérios mais importantes para mim são:

  • o método é chamado mais de uma vez em lugares diferentes?
  • o método é sofisticado o suficiente para exigir testes?

1

Se eu achar que o método privado é enorme ou complexo ou importante o suficiente para exigir seus próprios testes, basta colocá-lo em outra classe e torná-lo público lá (Método Objeto). Então eu posso testar facilmente o método anteriormente privado, mas agora público, que agora vive em sua própria classe.


1

Eu nunca entendo o conceito de teste de unidade, mas agora sei qual é o objetivo.

Um teste de unidade não é um teste completo . Portanto, não substitui o controle de qualidade e o teste manual. O conceito de TDD nesse aspecto está errado, pois você não pode testar tudo, incluindo métodos privados, mas também métodos que usam recursos (especialmente recursos que não temos controle). TDD está baseando toda a sua qualidade é algo que não poderia ser alcançado.

Um teste de unidade é mais um teste de pivô. Você marca algum pivô arbitrário e o resultado do pivô deve permanecer o mesmo.


1

Público x privado não é uma distinção útil para quais APIs devem chamar de seus testes, nem método vs. classe. A maioria das unidades testáveis ​​é visível em um contexto, mas oculta em outros.

O que importa é a cobertura e os custos. Você precisa minimizar os custos enquanto atinge as metas de cobertura do seu projeto (linha, ramificação, caminho, bloco, método, classe, classe de equivalência, caso de uso ... o que a equipe decidir).

Portanto, use ferramentas para garantir a cobertura e projete seus testes para causar menos custos (curto e longo prazo ).

Não faça testes mais caros do que o necessário. Se for mais barato testar apenas pontos de entrada públicos, faça isso. Se for mais barato testar métodos privados, faça isso.

À medida que for adquirindo experiência, você se tornará melhor em prever quando vale a pena refatorar para evitar custos de manutenção de teste a longo prazo.


0

Se o método for suficientemente significativo / complexo o suficiente, geralmente o tornarei "protegido" e o testarei. Alguns métodos serão deixados privados e testados implicitamente como parte de testes de unidade para os métodos público / protegido.


11
@VisibleForTesting é uma anotação para isso. Eu não iria relaxar encapsulamento para testar, em vez usar dp4j.com
simpatico

0

Vejo que muitas pessoas estão na mesma linha de pensamento: teste no nível público. mas não é isso que nossa equipe de controle de qualidade faz? Eles testam a entrada e a saída esperada. Se como desenvolvedores testamos apenas os métodos públicos, estamos simplesmente refazendo o trabalho do controle de qualidade e não agregando valor ao "teste de unidade".


A tendência atual é reduzir ou não ter equipe de controle de qualidade. Esses testes de unidade se tornam os testes automatizados que são executados toda vez que um engenheiro envia o código na ramificação principal. Mesmo com o controle de qualidade, não há como eles testarem todo o aplicativo tão rápido quanto os testes automatizados.
Patrick Desjardins

0

A resposta para "Devo testar métodos particulares?" As vezes". Normalmente você deve testar na interface de suas classes.

  • Um dos motivos é que você não precisa de cobertura dupla para um recurso.
  • Outro motivo é que, se você alterar métodos particulares, precisará atualizar cada teste para eles, mesmo que a interface do seu objeto não tenha sido alterada.

Aqui está um exemplo:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

Em RefactoredThingque você tem agora 5 testes, 2 dos quais você teve que atualizar para refatoração, mas a funcionalidade do seu objeto realmente não mudou. Então, digamos que as coisas são mais complexas que isso e você tem algum método que define a ordem da saída, como:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

Isso não deve ser executado por um usuário externo, mas sua classe de encapsulamento pode ser muito pesada para executar toda essa lógica repetidamente. Nesse caso, talvez você prefira extrair isso para uma classe separada, fornecer uma interface a essa classe e testar com ela.

E, finalmente, digamos que seu objeto principal seja super pesado, e o método seja bastante pequeno e você realmente precisa garantir que a saída esteja correta. Você está pensando: "Eu tenho que testar esse método particular!". Você talvez possa tornar seu objeto mais leve, passando alguns dos trabalhos pesados ​​como um parâmetro de inicialização? Então você pode passar algo mais leve e testar contra isso.


0

Não Você não deve testar os Métodos Privados, por quê? além disso, a popular estrutura de simulação, como o Mockito, não oferece suporte para testar métodos privados.


0

Um ponto principal é

Se testamos para garantir a correção da lógica, e um método privado está carregando uma lógica, devemos testá-la. Não é? Então, por que vamos pular isso?

Escrever testes com base na visibilidade dos métodos é uma idéia completamente irrelevante.

Inversamente

Por outro lado, chamar um método privado fora da classe original é um problema principal. E também há limitações para zombar de um método privado em algumas ferramentas de zombaria. (Ex: Mockito )

Embora existam algumas ferramentas como o Power Mock que suportam isso, é uma operação perigosa. O motivo é que ele precisa invadir a JVM para conseguir isso.

Uma solução alternativa que pode ser feita é (se você deseja gravar casos de teste para métodos particulares)

Declare esses métodos particulares como protegidos . Mas pode não ser conveniente para várias situações.


0

Não se trata apenas de métodos ou funções públicas ou privadas, mas de detalhes de implementação. Funções privadas são apenas um aspecto dos detalhes da implementação.

Afinal, o teste de unidade é uma abordagem de teste de caixa branca. Por exemplo, quem usa a análise de cobertura para identificar partes do código que foram negligenciadas nos testes até agora, entra nos detalhes da implementação.

A) Sim, você deve testar os detalhes da implementação:

Pense em uma função de classificação que, por razões de desempenho, use uma implementação privada do BubbleSort se houver até 10 elementos e uma implementação privada de uma abordagem de classificação diferente (por exemplo, heapsort) se houver mais de 10 elementos. A API pública é a de uma função de classificação. Sua suíte de testes, no entanto, utiliza melhor o conhecimento de que realmente existem dois algoritmos de classificação usados.

Neste exemplo, certamente, você pode executar os testes na API pública. No entanto, isso exigiria um número de casos de teste que executam a função de classificação com mais de 10 elementos, de modo que o algoritmo heapsort seja suficientemente bem testado. A existência desses casos de teste por si só é uma indicação de que o conjunto de testes está conectado aos detalhes de implementação da função.

Se os detalhes da implementação da função de classificação mudarem, talvez da maneira que o limite entre os dois algoritmos de classificação seja alterado ou que o heapsort seja substituído por mergesort ou o que for: Os testes existentes continuarão funcionando. Seu valor, no entanto, é questionável e eles provavelmente precisam ser reformulados para testar melhor a função de classificação alterada. Em outras palavras, haverá um esforço de manutenção, apesar do fato de os testes terem sido realizados na API pública.

B) Como testar detalhes de implementação

Uma razão pela qual muitas pessoas argumentam que não se deve testar funções privadas ou detalhes de implementação é que os detalhes de implementação têm mais probabilidade de mudar. Essa maior probabilidade de mudança, pelo menos, é um dos motivos para ocultar os detalhes da implementação por trás das interfaces.

Agora, suponha que a implementação por trás da interface contenha partes particulares maiores para as quais testes individuais na interface interna possam ser uma opção. Algumas pessoas argumentam que essas peças não devem ser testadas quando privadas, devem ser transformadas em algo público. Uma vez público, o teste de unidade desse código seria bom.

Isso é interessante: embora a interface fosse interna, era provável que ela mudasse, sendo um detalhe de implementação. Adotar a mesma interface, torná-la pública faz alguma transformação mágica, ou seja, transformá-la em uma interface com menor probabilidade de mudança. Obviamente, há alguma falha nessa argumentação.

No entanto, há alguma verdade por trás disso: ao testar os detalhes da implementação, em particular o uso de interfaces internas, deve-se esforçar-se por usar interfaces que provavelmente permanecerão estáveis. A probabilidade de alguma interface ser estável não é, no entanto, simplesmente decidível com base em se é pública ou privada. Nos projetos do mundo em que trabalho há algum tempo, as interfaces públicas também costumam mudar bastante, e muitas interfaces privadas permanecem intocadas há séculos.

Ainda assim, é uma boa regra geral usar a "porta da frente primeiro" (consulte http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Mas lembre-se de que é chamada "porta da frente primeiro" e não "porta da frente apenas".

C) Resumo

Teste também os detalhes da implementação. Prefira testar em interfaces estáveis ​​(públicas ou privadas). Se os detalhes da implementação mudarem, também é necessário revisar os testes na API pública. Transformar algo privado em público não altera magicamente sua estabilidade.


0

Sim, você deve testar métodos particulares, sempre que possível. Por quê? Para evitar uma explosão desnecessária no espaço de estados de casos de teste que acabam testando implicitamente as mesmas funções privadas repetidamente nas mesmas entradas. Vamos explicar o porquê com um exemplo.

Considere o seguinte exemplo um pouco artificial. Suponha que desejamos expor publicamente uma função que use 3 números inteiros e retorne true se e somente se esses 3 números inteiros forem primos. Podemos implementá-lo assim:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

Agora, se adotássemos a abordagem estrita de que apenas funções públicas deveriam ser testadas, poderíamos apenas testar allPrimee não isPrimeou andAll.

Como testador, que poderia estar interessado em cinco possibilidades para cada argumento: < 0, = 0, = 1, prime > 1, not prime > 1. Mas, para ser completo, teríamos que ver também como todas as combinações de argumentos se encaixam. Portanto, são 5*5*5125 casos de teste que precisaríamos testar completamente essa função, de acordo com nossas intuições.

Por outro lado, se pudéssemos testar as funções privadas, poderíamos cobrir tanto terreno com menos casos de teste. Precisávamos de apenas 5 casos de teste isPrimepara testar no mesmo nível da nossa intuição anterior. E pela hipótese do pequeno escopo proposta por Daniel Jackson, precisaríamos apenas testar a andAllfunção até um pequeno comprimento, por exemplo, 3 ou 4. O que seria no máximo 16 testes a mais. Então 21 testes no total. Em vez de 125. É claro que provavelmente quereríamos executar alguns testes allPrime, mas não nos sentiríamos tão obrigados a cobrir exaustivamente todas as 125 combinações de cenários de entrada com os quais dissemos que nos importávamos. Apenas alguns caminhos felizes.

Um exemplo artificial, com certeza, mas era necessário para uma demonstração clara. E o padrão se estende ao software real. As funções privadas geralmente são os blocos de construção de nível mais baixo e, portanto, são frequentemente combinadas para gerar uma lógica de nível mais alto. Ou seja, em níveis mais altos, temos mais repetições das coisas de nível inferior devido às várias combinações.


Primeiro, você não precisa testar combinações como essa com funções puras, como mostrou. As chamadas para isPrimesão verdadeiramente independentes; portanto, testar todas as combinações às cegas não tem propósito. Em segundo lugar, marcar uma função pura chamada isPrimeprivada viola tantas regras de design que eu nem sei por onde começar. isPrimemuito claramente deve ser uma função pública. Dito isto, eu entendo o que você está dizendo, independentemente deste exemplo extremamente pobre. No entanto, foi construído com base na premissa de que você deseja fazer testes combinados, quando em sistemas de software reais isso raramente é uma boa idéia.
Matt Messersmith

Matt sim, o exemplo não é o ideal, eu vou te dar isso. Mas o princípio deve ser óbvio.
Colm Bhandal 23/08/19

A premissa não é exatamente o que você deseja fazer testes combinados. É o que você precisaria, se você se restringisse a testar apenas peças públicas do quebra-cabeça. Há casos em que você deseja tornar privada uma função pura para aderir aos princípios de encapsulamento adequados. E essa função privada pura pode ser usada por entidades públicas. De maneira combinatória, talvez com outras funções privadas puras. Nesse caso, seguindo o dogma que você não deve testar em particular, você será forçado a fazer testes combinados na função pública, em vez de fazer testes modulares dos componentes privados.
Colm Bhandal 23/08/19

0

Você também pode tornar seu método package-private, ou seja, padrão, e deve poder fazer testes unitários, a menos que seja necessário que seja privado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.