Tornar público um método privado para testá-lo por unidade ... boa ideia?

301

Nota do moderador: já existem 39 respostas postadas aqui (algumas foram excluídas). Antes de postar sua resposta, considere se você pode ou não adicionar algo significativo à discussão. É mais do que provável que você apenas repita o que alguém já disse.


Ocasionalmente, preciso me tornar um método privado em uma classe pública apenas para escrever alguns testes de unidade para ele.

Normalmente, isso ocorre porque o método contém lógica compartilhada entre outros métodos da classe e é mais organizado testar a lógica por conta própria, ou outro motivo pode ser possível: desejo testar a lógica usada em encadeamentos síncronos sem ter que se preocupar com problemas de encadeamento .

Outras pessoas se veem fazendo isso, porque eu realmente não gosto de fazer isso? Pessoalmente, acho que os bônus superam os problemas de tornar público um método que realmente não fornece nenhum serviço fora da classe ...

ATUALIZAR

Obrigado por responder a todos, parece ter despertado o interesse das pessoas. Penso que o consenso geral de que os testes devem ocorrer por meio da API pública, pois é a única maneira de uma classe ser usada, e eu concordo com isso. Os dois casos que eu mencionei acima, onde eu faria isso acima, eram casos incomuns e achei que os benefícios de fazer isso valeram a pena.

No entanto, posso ver que todos apontam que isso nunca deveria realmente acontecer. E, quando penso um pouco mais sobre isso, acho que mudar seu código para acomodar testes é uma péssima idéia - afinal, suponho que o teste seja uma ferramenta de suporte e que mude um sistema para 'suportar uma ferramenta de suporte', se quiser, é flagrante má prática.

jcvandan
fonte
2
"mudar um sistema para 'suportar uma ferramenta de suporte', se você preferir, é uma prática flagrante". Bem, sim, mais ou menos. Mas OTOH, se o seu sistema não funcionar bem com as ferramentas estabelecidas, talvez algo esteja errado com o sistema.
Thilo
1
Como esta duplicata não é do stackoverflow.com/questions/105007/do-you-test-private-method ?
Sanghyun Lee 17/08/11
1
Não é uma resposta, mas você sempre pode acessá-los por reflexão.
Zano
33
Não, você não deve expô-los. Em vez disso, você deve testar se sua classe se comporta como deveria, por meio de sua API pública. E se a exposição interna é realmente a única opção (o que duvido), você deve pelo menos proteger o pacote de acessadores, para que apenas as classes do mesmo pacote (como deveria ser o seu teste) possam acessá-las.
JB Nizet
4
Sim, ele é. É perfeitamente aceitável fornecer visibilidade a um método package-private (padrão) para torná-lo acessível a partir de testes de unidade. Bibliotecas como o Guava até fornecem uma @VisibileForTestinganotação para fazer esses métodos - eu recomendo que você faça isso para que o motivo do método não privateseja devidamente documentado.
Boris the Spider

Respostas:

186

Nota:
Esta resposta foi postada originalmente para a pergunta O teste de unidade sozinho é sempre um bom motivo para expor variáveis ​​de instância privada por meio de getters? que foi mesclado a este, por isso pode ser um pouco específico para o caso de uso apresentado lá.

Como uma declaração geral, geralmente sou a favor de refatorar o código de "produção" para facilitar o teste. No entanto, eu não acho que seria uma boa ligação aqui. Um bom teste de unidade (geralmente) não deve se preocupar com os detalhes de implementação da classe, apenas com seu comportamento visível. Em vez de expor as pilhas internas ao teste, você pode testar se a classe retorna as páginas na ordem em que você espera após chamar first()ou last().

Por exemplo, considere este pseudocódigo:

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}
Mureinik
fonte
8
Concordo plenamente com a sua resposta. Muitos desenvolvedores ficariam tentados a usar o modificador de acesso padrão e justificariam que boas estruturas de código-fonte aberto usem essa estratégia para que ela esteja correta. Só porque você pode, não significa que deveria. +1
CKing
6
Embora outros tenham fornecido informações úteis, devo dizer que esta é a solução que acho mais apropriada para esse problema. Ele mantém os testes completamente independentes de como o comportamento pretendido é alcançado internamente. +10!
Gitahi Ng'ang'a
A propósito, tudo bem que, no exemplo fornecido pelo @Mureinik aqui, os métodos de teste acabem cobrindo mais do que o método real em teste? Por exemplo, testFirst () chama next (), que não é realmente o método testado aqui, primeiro () é. Não seria mais limpo e claro ter apenas um método de teste para todos os quatro métodos de navegação?
Gitahi Ng'ang'a
1
Embora esse seja o ideal, há momentos em que você precisa verificar se um componente fez algo da maneira certa, não apenas pelo sucesso. Isso pode ser mais difícil de testar na caixa preta. Às vezes você precisa testar em caixa branca um componente.
28515 Peter Lawrey
1
Criando getters apenas para testar a unidade? Definitivamente, isso contraria o princípio da OOP / Object Thinking , pois a interface deve expor o comportamento, não os dados.
IntelliData 26/01
151

Pessoalmente, prefiro o teste de unidade usando a API pública e certamente nunca tornaria o método privado público apenas para facilitar o teste.

Se você realmente deseja testar o método privado isoladamente, em Java, você pode usar o Easymock / Powermock para permitir isso.

Você precisa ser pragmático e também deve estar ciente das razões pelas quais as coisas são difíceis de testar.

' Ouça os testes ' - se é difícil testar, isso está lhe dizendo algo sobre seu design? Você poderia refatorar para onde um teste para esse método seria trivial e facilmente coberto por testes através da API pública?

Aqui está o que Michael Feathers tem a dizer em ' Trabalhando efetivamente com o código legado "

"Muitas pessoas gastam muito tempo tentando descobrir como contornar esse problema ... a resposta real é que, se você tem vontade de testar um método privado, o método não deve ser privado; se tornar o método público incomoda você, é provável que seja porque faz parte de uma responsabilidade separada; deve estar em outra classe. " [ Trabalhando efetivamente com o código legado (2005) por M. Feathers]

em branco
fonte
5
+1 para EasyMock / PowerMock e nunca fazer um método público privada
Leonard Brünings
51
+1 Para "Ouvir os testes". Se você sentir necessidade de testar um método privado, é provável que a lógica desse método seja tão complexa que deveria estar em uma classe auxiliar separada. Então você pode testar a classe auxiliar.
Phil
2
'Ouvir os testes' parece estar desativado. Aqui está um link em cache: webcache.googleusercontent.com/…
Jess Telford
1
Eu concordo com @Phil (especialmente a referência do livro), mas muitas vezes me encontro em uma situação de galinha / ovo com código legado. Sei que esse método pertence a outra classe, no entanto, não me refiro 100% à refatoração sem testes em primeiro lugar.
Kevin
Eu concordo médico. Se você precisar testar um método privado, provavelmente deve estar em outra classe. Você pode observar que a outra classe / classe auxiliar desse método privado provavelmente se tornará pública / protegida.
rupweb
67

Como outros já disseram, é um pouco suspeito estar testando métodos privados; unit test a interface pública, não os detalhes da implementação privada.

Dito isto, a técnica que eu uso quando quero testar alguma coisa privada em C # é reduzir a proteção de acessibilidade de privada para interna e marcar o assembly de teste de unidade como um amigo usando InternalsVisibleTo . O conjunto de teste da unidade poderá tratar os internos como públicos, mas você não precisa se preocupar em adicionar acidentalmente sua área de superfície pública.

Eric Lippert
fonte
Também uso essa técnica, mas como último recurso para o código herdado normalmente. Se você precisar testar o código privado, há uma classe ausente / a classe em teste está fazendo muito. Extraia o código para uma nova classe que você pode testar isoladamente. Truques como esse devem ser usados ​​raramente.
Finglas
Mas extrair uma classe e testar apenas isso significa que seus testes perdem o conhecimento de que a classe original está realmente usando o código extraído. Como você recomenda que você preencha essa lacuna em seus testes? E se a resposta é apenas testar a interface pública da classe original de uma maneira que faça com que a lógica extraída seja usada, por que se preocupar em extraí-la em primeiro lugar? (Eu não vou querer soar confronto aqui, btw - Acabei de saber sempre como pessoas equilibrar esses trade-offs.)
Mal Ross
@mal Eu teria um teste de colaboração. Em outras palavras, uma simulação para verificar a nova classe foi invocada corretamente. Veja minha resposta abaixo.
Finglas
Alguém poderia escrever uma classe de teste que herda da classe com lógica interna que você deseja testar e fornecer um método público a ser usado para testar a lógica interna?
sholsinger
1
@ sholsinger: em C # uma classe pública não pode herdar de uma classe interna.
Eric Lippert
45

Muitas respostas sugerem apenas testar a interface pública, mas IMHO isso não é realista - se um método faz algo que leva cinco etapas, você deve testar essas cinco etapas separadamente, nem todas juntas. Isso requer testar todos os cinco métodos, que (exceto os testes) podem ser private.

A maneira usual de testar métodos "privados" é dar a cada classe sua própria interface e criar os métodos "privados" public, mas não os inclui na interface. Dessa forma, eles ainda podem ser testados, mas não incham a interface.

Sim, isso resultará em inchaço de arquivo e classe.

Sim, isso torna os especificadores publice privateredundantes.

Sim, isso é um pé no saco.

Infelizmente, este é um dos muitos sacrifícios que fazemos para tornar o código testável . Talvez uma linguagem futura (ou mesmo uma versão futura do C # / Java) tenha recursos para tornar a testabilidade de classe e módulo mais conveniente; mas, enquanto isso, temos que pular esses aros.


Alguns argumentam que cada uma dessas etapas deve ser de sua própria classe , mas eu discordo - se todas compartilham o estado, não há razão para criar cinco classes separadas, onde cinco métodos o fariam. Pior ainda, isso resulta em inchaço de arquivos e classes. Além disso , ele infecta a API pública do seu módulo - todas essas classes devem ser necessariamente publicse você quiser testá-las em outro módulo (ou incluir o código de teste no mesmo módulo, o que significa enviar o código de teste com o seu produto) .

BlueRaja - Danny Pflughoeft
fonte
2
boa resposta para a primeira metade
cmcginty
7
+1: a melhor resposta para mim. Se um fabricante de automóveis não testar uma parte de seu carro pelo mesmo motivo, não tenho certeza de que alguém o compraria. Parte importante do código deve ser testada, mesmo que devamos alterá-lo para público.
Tae-Sung Shin
1
Resposta muito boa. Você tem meu voto caloroso. Eu adicionei uma resposta. Isso pode lhe interessar.
Davidxxx 31/12/19
29

Um teste de unidade deve testar o contrato público, a única maneira de como uma classe pode ser usada em outras partes do código. Um método privado é o detalhamento da implementação; você não deve testá-lo, pois a API pública funciona corretamente, a implementação não importa e pode ser alterada sem alterações nos casos de teste.

kan
fonte
+1, e uma resposta realted: stackoverflow.com/questions/4603847/…
Raedwald 16/08/11
Caso, a rara situação que decidi usar privates em um caso de teste foi quando eu estava verificando a correção de um objeto - se ele fechou todos os manipuladores JNI. Obviamente, ele não é exposto como API pública; no entanto, se isso não acontecer, ocorrerá um vazamento de memória. Mas, engraçado, mais tarde eu me livrei dele também depois de introduzir um detector de vazamento de memória como parte da API pública.
kan
11
Essa é uma lógica falha. O objetivo do teste de unidade é detectar erros mais cedo ou mais tarde. Ao não testar métodos particulares, você deixa lacunas nos testes que podem não ser descobertas até muito mais tarde. No caso em que o método privado é complexo e não é facilmente exercido por uma interface pública, ele deve ser testado em unidade.
cmcginty
6
@ Casey: se o método privado não é exercido por uma interface pública, por que existe?
Thilo
2
@DaSilva_Ireland: Será difícil manter os casos de teste no futuro. O único caso de uso de classe real é via método público. Ou seja, todos os outros códigos poderiam usar a classe apenas pela API pública. Portanto, se você deseja alterar a implementação e o caso de teste de método privado, isso não significa que você está fazendo algo errado; além disso, se você estiver testando apenas o privado, mas não o público na íntegra, ele não abordará alguns possíveis bugs. . Idealmente, um caso de teste deve cobrir todos os casos possíveis e apenas os casos de uso real da classe.
kan
22

Na IMO, você deve escrever seus testes sem fazer suposições profundas sobre como sua classe foi implementada por dentro. Você provavelmente deseja refatorá-lo posteriormente usando outro modelo interno, mas ainda dando as mesmas garantias que a implementação anterior fornece.

Tendo isso em mente, sugiro que você se concentre em testar se o seu contrato ainda é válido, independentemente da implementação interna da sua classe. Teste de propriedade de suas APIs públicas.

SimY4
fonte
6
Concordo que os testes de unidade que não precisam ser reescritos após uma refatoração do código são melhores. E não apenas pelo tempo que você gastaria reescrevendo os testes de unidade. Um motivo muito mais importante é que considero o objetivo mais importante dos testes de unidade o fato de seu código ainda se comportar corretamente após uma refatoração. Se você precisar reescrever todos os seus testes de unidade após uma refatoração, perderá o benefício dos testes de unidade.
kasperd
20

Que tal torná-lo privado? Em seguida, seu código de teste pode vê-lo (e outras classes no seu pacote), mas ainda está oculto para seus usuários.

Mas, na verdade, você não deve testar métodos privados. Esses são detalhes de implementação e não fazem parte do contrato. Tudo o que eles fazem deve ser coberto chamando os métodos públicos (se eles tiverem um código que não seja exercido pelos métodos públicos, isso deverá ser feito). Se o código privado é muito complexo, a classe provavelmente está fazendo muitas coisas e quer refatorar.

Tornar público um método é um grande compromisso. Depois que você fizer isso, as pessoas poderão usá-lo e você não poderá mais mudá-las.

Thilo
fonte
+1 se você precisar testar suas partes privadas, provavelmente está faltando uma aula
jk.
+1 para a turma ausente. Fico feliz em ver que outras pessoas não pulam na onda "marque interno" imediatamente.
Finglas
Rapaz, eu tive que percorrer um longo caminho para encontrar a resposta privada do pacote.
Bill K
17

Atualização : eu adicionei uma resposta mais expandida e mais completa a esta pergunta em vários outros lugares. Isso pode ser encontrado no meu blog .

Se eu precisar tornar público algo para testá-lo, isso geralmente indica que o sistema em teste não está seguindo o Princípio Único de Responsabilidade . Portanto, há uma classe ausente que deve ser introduzida. Depois de extrair o código em uma nova classe, torne-o público. Agora você pode testar facilmente e está seguindo o SRP. Sua outra classe simplesmente precisa invocar essa nova classe via composição.

Tornar métodos públicos / usar truques de idioma, como marcar o código como visível para os assemblies de teste, deve sempre ser o último recurso.

Por exemplo:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

Refatore isso introduzindo um objeto validador.

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

Agora tudo o que precisamos fazer é testar se o validador foi chamado corretamente. O processo real de validação (a lógica anteriormente privada) pode ser testado em puro isolamento. Não será necessário configurar testes complexos para garantir que essa validação seja aprovada.

Finglas
fonte
1
Pessoalmente, acho que o SRP pode ser levado longe demais, por exemplo, se eu escrever um serviço de conta que lide com a atualização / criação / exclusão de contas em um aplicativo, você está me dizendo que deve criar uma classe separada para cada operação? E a validação, por exemplo, realmente vale a pena extrair a lógica de validação para outra classe quando ela nunca será usada em nenhum outro lugar? imo que apenas torna o código menos legível, menos conciso e empacota seu aplicativo com classes supérfluas!
Jcvandan
1
Tudo depende do cenário. A definição de uma pessoa de "fazer uma coisa" pode ser diferente para outras. Nesse caso, claramente o código privado que você deseja testar é complexo / difícil de configurar. O próprio fato de você desejar testá-lo prova que está fazendo muito. Portanto, sim, ter um novo objeto será o ideal.
Finglas
1
Quanto a tornar seu código menos legível, discordo totalmente. Ter uma classe para validação é mais fácil de ler do que ter a validação incorporada no outro objeto. Você ainda tem a mesma quantidade de código, mas não está aninhado em uma classe grande. Eu prefiro ter várias classes que fazem uma coisa muito bem do que uma classe grande.
Finglas
4
O @dormisher, em relação ao serviço da sua conta, considera o SRP como um "único motivo para mudar". Se suas operações CRUD mudassem naturalmente juntas, elas se ajustariam ao SRP. Normalmente, você os altera porque o esquema do banco de dados pode mudar, o que afetaria naturalmente a inserção e a atualização (embora talvez nem sempre seja excluído).
Anthony Pegram
@finglas o que você pensa sobre isso: stackoverflow.com/questions/29354729/… . Não tenho vontade de testar o método privado, talvez não deva separá-lo em uma classe, mas ele está sendo usado em outros 2 métodos públicos, por isso acabaria testando duas vezes a mesma lógica.
Rodrigo Ruiz
13

Algumas ótimas respostas. Uma coisa que não vi mencionada é que, com o desenvolvimento orientado a teste (TDD), métodos privados são criados durante a fase de refatoração (veja Método de extração para obter um exemplo de padrão de refatoração) e, portanto, já deve ter a cobertura de teste necessária. . Se feito corretamente (e, é claro, você terá uma mescla de opiniões quando se trata de correção), não precisa se preocupar em tornar público um método privado apenas para poder testá-lo.

csano
fonte
11

Por que não dividir o algoritmo de gerenciamento de pilha em uma classe de utilitário? A classe de utilitário pode gerenciar as pilhas e fornecer acessadores públicos. Seus testes de unidade podem ser focados nos detalhes da implementação. Testes profundos para classes complicadas algoritmicamente são muito úteis para eliminar os casos extremos e garantir a cobertura.

Em seguida, sua classe atual pode delegar de maneira limpa para a classe de utilitário, sem expor nenhum detalhe de implementação. Seus testes estarão relacionados ao requisito de paginação, conforme recomendado por outros.

Alfred Armstrong
fonte
10

Em java, há também a opção de torná-lo privado (ou seja, deixando de fora o modificador de visibilidade). Se seus testes de unidade estiverem no mesmo pacote que a classe que está sendo testada, ele poderá ver esses métodos e será um pouco mais seguro do que tornar o método completamente público.

Tom Jefferys
fonte
10

Métodos particulares são geralmente usados ​​como métodos "auxiliares". Portanto, eles retornam apenas valores básicos e nunca operam em instâncias específicas de objetos.

Você tem algumas opções se quiser testá-las.

  • Use reflexão
  • Conceder acesso ao pacote de métodos

Como alternativa, você pode criar uma nova classe com o método auxiliar como um método público, se for um candidato suficientemente bom para uma nova classe.

Há um artigo muito bom aqui sobre isso.

adamjmarkham
fonte
1
Comentário justo. Era apenas uma opção, talvez ruim.
adamjmarkham
@Finglas Por que testar código privado usando reflexão é uma má idéia?
Anshul 24/06
Os métodos privados do @Anshul são detalhes de implementação e não comportamento. Em outras palavras, eles mudam. Os nomes mudam. Os parâmetros mudam. O conteúdo muda. Isso significa que esses testes serão interrompidos o tempo todo. Os métodos públicos, por outro lado, são muito mais estáveis ​​em termos de API, portanto, são mais resilientes. Fornecendo seus testes em métodos públicos para verificar o comportamento e não os detalhes da implementação, você deve ser bom.
Finglas
1
@Finglas Por que você não deseja testar os detalhes da implementação? É um código que precisa funcionar e, se ele mudar com mais frequência, você espera que ele quebre com mais frequência, o que é mais um motivo para testá-lo mais do que a API pública. Depois que sua base de código estiver estável, digamos que em algum momento no futuro alguém apareça e mude um método privado que interrompa o funcionamento interno da sua classe. Um teste que testa os internos de sua turma imediatamente diz que eles quebraram alguma coisa. Sim, isso tornaria a manutenção de seus testes mais difícil, mas isso é algo que você espera dos testes de cobertura total.
precisa
1
Os detalhes da implementação do teste @Finglas não estão errados. Essa é sua opinião, mas esse é um tópico amplamente debatido. Testar os componentes internos oferece algumas vantagens, mesmo que elas não sejam necessárias para uma cobertura completa. Seus testes são mais focados porque você está testando os métodos privados diretamente, em vez de passar pela API pública, que pode confundir seu teste. O teste negativo é mais fácil porque você pode invalidar manualmente os membros privados e garantir que a API pública retorne erros apropriados em resposta ao estado interno inconsistente. Existem mais exemplos.
precisa
9

Se você estiver usando C #, poderá tornar o método interno. Dessa forma, você não polui a API pública.

Em seguida, adicione atributo à dll

[assembly: InternalsVisibleTo ("MyTestAssembly")]

Agora todos os métodos estão visíveis no seu projeto MyTestAssembly. Talvez não seja perfeito, mas é melhor do que tornar público o método privado apenas para testá-lo.

Piotr Perak
fonte
7

Use a reflexão para acessar as variáveis ​​privadas, se necessário.

Mas, na verdade, você não se importa com o estado interno da classe, apenas deseja testar se os métodos públicos retornam o que você espera nas situações que você pode antecipar.

Daniel Alexiuc
fonte
O que você faria com um método nulo?
IntelliData 26/01
@IntelliData Use a reflexão para acessar as variáveis ​​privadas, se necessário.
Daniel Alexiuc
6

Eu diria que é uma péssima idéia, pois não tenho certeza se você obtém algum benefício ou potencial problema no futuro. Se você está alterando o contrato de uma chamada, apenas para testar um método privado, não está testando a classe em como ela seria usada, mas criando um cenário artificial que você nunca pretendeu que acontecesse.

Além disso, declarando o método como público, o que significa dizer que em seis meses (depois de esquecer que a única razão para tornar um método público é o teste) você (ou se você entregou o projeto) alguém completamente diferente venceu use-o, levando a conseqüências potencialmente não intencionais e / ou a um pesadelo de manutenção.

beny23
fonte
1
que tal se a classe é a implementação de um contrato de serviço e só é usada via interface - desta maneira, mesmo que o método privado tenha sido tornado público, ele não será chamado de qualquer maneira, pois não é visível
jcvandan
Eu ainda gostaria de testar a classe usando a API pública (interface), porque, caso contrário, se você refatorar a classe para dividir o método interno, terá que alterar os testes, o que significa que você precisará gastar um pouco esforço para garantir que os novos testes funcionem da mesma forma que os testes antigos.
precisa saber é
Além disso, se a única maneira de garantir que a cobertura do teste inclua todos os casos extremos dentro de um método privado é chamá-lo diretamente, pode ser indicativo que a complexidade da classe justifique refatoração para simplificá-lo.
beny23
@dormisher - Se a classe for usada apenas pela interface, você só precisará testar se os métodos da interface pública se comportam corretamente. Se os métodos públicos fazem o que devem, por que você se importa com os privados?
Andrzej Doyle
@Andrzej - Suponho que não deveria, mas existem situações em que acredito que possam ser válidas, por exemplo, um método privado que valida o estado do modelo, pode valer a pena testar um método como este - mas um método como esse deve ser testável via a interface pública de qualquer maneira
jcvandan
6

em termos de teste de unidade, você definitivamente não deve adicionar mais métodos; Acredito que é melhor você fazer um caso de teste sobre o seu first()método, que seria chamado antes de cada teste; então você pode chamar várias vezes o - next(), previous()e last()para ver se os resultados corresponder à sua expectativa. Acho que se você não adicionar mais métodos à sua classe (apenas para fins de teste), você seguirá o princípio da "caixa preta" de teste;

Johny
fonte
5

Primeiro, veja se o método deve ser extraído para outra classe e tornado público. Se não for esse o caso, torne-o protegido por pacotes e anote em Java com @VisibleForTesting .

Noel Yap
fonte
5

Na sua atualização, você diz que é bom apenas testar usando a API pública. Na verdade, existem duas escolas aqui.

  1. Teste de caixa preta

    A escola da caixa preta diz que a turma deve ser considerada uma caixa preta que ninguém pode ver a implementação dentro dela. A única maneira de testar isso é através da API pública - assim como o usuário da classe o usará.

  2. teste de caixa branca.

    A escola de caixa branca pensa que é natural usar o conhecimento sobre a implementação da turma e depois testá-la para saber se ela funciona como deveria.

Eu realmente não posso tomar parte na discussão. Eu apenas pensei que seria interessante saber que existem duas maneiras distintas de testar uma classe (ou uma biblioteca ou qualquer outra coisa).

bengtb
fonte
4

Na verdade, existem situações em que você deve fazer isso (por exemplo, ao implementar alguns algoritmos complexos). Basta fazê-lo como pacote privado e isso será suficiente. Mas, na maioria dos casos, provavelmente você tem classes muito complexas, o que exige levar em consideração a lógica para outras classes.

Stanislav Bashkyrtsev
fonte
4

Métodos particulares que você deseja testar isoladamente são uma indicação de que há outro "conceito" oculto em sua classe. Extraia esse "conceito" para sua própria classe e teste-o como uma "unidade" separada.

Dê uma olhada neste vídeo para uma visão realmente interessante sobre o assunto.

Jordão
fonte
4

Você nunca deve deixar seus testes ditarem seu código. Eu não estou falando sobre TDD ou outros DDs, quero dizer, exatamente o que você está perguntando. Seu aplicativo precisa desses métodos para ser público. Se isso acontecer, teste-os. Caso contrário, não os publique apenas para teste. Mesmo com variáveis ​​e outros. Permita que as necessidades do seu aplicativo ditem o código e deixe seus testes testarem se a necessidade é atendida. (Novamente, não quero dizer testar primeiro ou não, mudar a estrutura de classes para atingir uma meta de teste).

Em vez disso, você deve "testar mais alto". Teste o método que chama o método privado. Mas seus testes devem testar suas necessidades de aplicativos e não suas "decisões de implementação".

Por exemplo (pseudo-código do bod aqui);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

Não há motivo para testar "adicionar", você pode testar "livros".

Nunca deixe seus testes tomarem decisões de design de código para você. Teste se você obtém o resultado esperado, não como obtém esse resultado.

coteyr
fonte
1
"Nunca deixe seus testes tomarem decisões de design de código para você" - devo discordar. Ser difícil de testar é um cheiro de código. Se você não pode testá-lo, como você sabe que não está quebrado?
Alfred Armstrong
Para esclarecimento, concordo que você não deve alterar o design da API externa de uma biblioteca, por exemplo. Mas você pode precisar refatorar para torná-lo mais testável.
Alfred Armstrong
Descobri que, se você precisar refatorar para testar, não é com esse cheiro de código que você deve se preocupar. Você tem algo mais errado. É claro que é uma experiência pessoal, e nós dois provavelmente poderíamos argumentar com dados sólidos nos dois sentidos. Eu apenas nunca tive "difícil de testar" que não era "design pobre" em primeiro lugar.
coteyr
Esta solução é bom para um método como livros que retorna um valor - você pode verificar o tipo de retorno em uma assert ; mas como você verificaria um método nulo ?
IntelliData 26/01
métodos nulos fazem alguma coisa ou eles não precisam existir. Verifique o que eles fazem,
coteyr 30/03
2

Costumo concordar que os bônus de testá-la superam os problemas de aumentar a visibilidade de alguns dos membros. Uma pequena melhoria é torná-lo protegido e virtual e substituí-lo em uma classe de teste para expô-lo.

Como alternativa, se é a funcionalidade que você deseja testar separadamente, isso não sugere um objeto ausente do seu design? Talvez você possa colocá-lo em uma classe testável separada ... então sua classe existente apenas delega para uma instância dessa nova classe.

IanR
fonte
2

Geralmente mantenho as classes de teste no mesmo projeto / montagem que as classes em teste.
Dessa forma, eu só preciso de internalvisibilidade para tornar as funções / classes testáveis.

Isso complica um pouco o processo de construção, que precisa filtrar as classes de teste. Consigo isso nomeando todas as minhas classes de teste TestedClassTeste usando um regex para filtrar essas classes.

Obviamente, isso só se aplica à parte C # / .NET da sua pergunta

yas4891
fonte
2

Eu, muitas vezes, adicionar um método chamado algo como validate, verify, check, etc, a uma classe de modo que ele pode ser chamado para testar o estado interno de um objeto.

Às vezes, esse método é envolvido em um bloco ifdef (eu escrevo principalmente em C ++) para que não seja compilado para lançamento. Mas geralmente é útil na versão fornecer métodos de validação que percorrem as árvores de objetos do programa verificando as coisas.

Zan Lynx
fonte
2

O Goiaba possui uma anotação @VisibleForTesting para métodos de marcação com escopo ampliado (pacote ou público) que eles teriam. Eu uso uma anotação @Private para a mesma coisa.

Embora a API pública deva ser testada, às vezes é conveniente e sensato obter informações que normalmente não seriam públicas.

Quando:

  • uma classe é tornada significativamente menos legível, in toto, dividindo-a em várias classes,
  • apenas para torná-lo mais testável,
  • e fornecer algum acesso de teste às entranhas faria o truque

parece que a religião está superando a engenharia.

Ed Staub
fonte
2

Normalmente deixo esses métodos como protectede coloco o teste de unidade no mesmo pacote (mas em outro projeto ou pasta de origem), onde eles podem acessar todos os métodos protegidos porque o carregador de classes os colocará no mesmo espaço de nome.

hiergiltdiestfu
fonte
2

Não, porque existem maneiras melhores de esfolar esse gato.

Alguns chicotes de teste de unidade dependem de macros na definição de classe que se expandem automaticamente para criar ganchos quando incorporadas no modo de teste. Muito estilo C, mas funciona.

Um idioma OO mais fácil é tornar o que você deseja testar "protegido" em vez de "privado". O equipamento de teste é herdado da classe em teste e pode acessar todos os membros protegidos.

Ou você escolhe a opção "amigo". Pessoalmente, esse é o recurso do C ++ que eu menos gosto porque quebra as regras de encapsulamento, mas é necessário como o C ++ implementa alguns recursos, então, ei.

De qualquer forma, se você estiver testando a unidade, provavelmente precisará injetar valores nesses membros. Mensagens de texto em caixa branca são perfeitamente válidas. E isso realmente iria quebrar seu encapsulamento.

Graham
fonte
2

No .Net, há uma classe especial chamada PrivateObjectdeign especificamente para permitir que você acesse métodos privados de uma classe.

Veja mais sobre isso no MSDN ou aqui no Stack Overflow

(Gostaria de saber que ninguém o mencionou até agora.)

Existem situações em que isso não é suficiente; nesse caso, você terá que usar a reflexão.

Ainda assim, eu adotaria a recomendação geral de não testar métodos privados, no entanto, como sempre, sempre há exceções.

yoel halb
fonte
1

Como é amplamente observado pelos comentários de outras pessoas, os testes de unidade devem se concentrar na API pública. No entanto, prós / contras e justificativa à parte, você pode chamar métodos privados em um teste de unidade usando reflexão. Obviamente, você precisaria garantir que sua segurança JRE o permitisse. Chamar métodos privados é algo que o Spring Framework emprega com seus ReflectionUtils (consulte o makeAccessible(Method)método).

Aqui está uma pequena classe de exemplo com um método de instância privada.

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

E uma classe de exemplo que executa o método de instância privada.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

A execução de B será impressa. Doing something private. Se você realmente precisar, a reflexão pode ser usada em testes de unidade para acessar métodos de instância privada.

Dan Cruz
fonte
0

Pessoalmente, tenho os mesmos problemas ao testar métodos privados e isso ocorre porque algumas das ferramentas de teste são limitadas. Não é bom que seu design seja guiado por ferramentas limitadas, se elas não responderem à sua necessidade, altere a ferramenta e não o design. Como você está pedindo C #, não posso propor boas ferramentas de teste, mas para Java existem duas ferramentas poderosas: TestNG e PowerMock, e você pode encontrar as ferramentas de teste correspondentes para a plataforma .NET

Xoke
fonte