Como você testa métodos privados com NUnit?

104

Estou me perguntando como usar o NUnit corretamente. Primeiro, criei um projeto de teste separado que usa meu projeto principal como referência. Mas, nesse caso, não consigo testar métodos privados. Meu palpite é que preciso incluir meu código de teste em meu código principal ?! - Essa não parece ser a maneira correta de fazer isso. (Não gosto da ideia de enviar código com testes nele.)

Como você testa métodos privados com NUnit?

MrFox
fonte

Respostas:

74

Geralmente, o teste de unidade aborda a interface pública de uma classe, na teoria de que a implementação é imaterial, desde que os resultados sejam corretos do ponto de vista do cliente.

Portanto, o NUnit não fornece nenhum mecanismo para testar membros não públicos.

arpão
fonte
1
1 Acabei de me deparar com esse problema e, no meu caso, há um algoritmo de "mapeamento" que acontece entre o significado privado e o público se eu fosse fazer um Teste de Unidade para o público, seria na verdade um teste de integração. Nesse cenário, acho que tentar escrever os pontos de teste para um problema de design no código. nesse caso, provavelmente devo criar uma classe diferente que execute esse método "privado".
Andy
140
Eu discordo totalmente deste argumento. O teste de unidade não é sobre a classe, é sobre o código . Se eu tivesse uma classe com um método público e dez privados usados ​​para criar o resultado do público, não teria como garantir que cada método privado funcionasse da maneira pretendida. Eu poderia tentar criar casos de teste no método público que atingisse o método privado certo da maneira certa. Mas eu não tenho ideia se o método privado foi realmente chamado, a menos que eu confie que o método público nunca será alterado. Métodos privados devem ser privados para usuários de produção de código, não para o autor.
Quango
3
@Quango, em uma configuração de teste padrão, o "autor" é um usuário de produção do código. No entanto, se você não sentir o cheiro de um problema de design, terá opções para testar métodos não públicos sem alterar a classe. System.Reflectionpermite que você acesse e invoque métodos não públicos usando sinalizadores de ligação, para que você possa hackear o NUnit ou configurar sua própria estrutura. Ou (mais fácil, eu acho), você poderia configurar um sinalizador de tempo de compilação (#if TESTING) para alterar os modificadores de acesso, permitindo que você use estruturas existentes.
harpo
23
Um método privado é um detalhe de implementação. Grande parte do objetivo de ter testes de unidade é permitir a refatoração da implementação sem alterar o comportamento . Se os métodos privados se tornam tão complicados que alguém gostaria de cobri-los com testes individualmente, então este lema pode ser usado: "Um método privado pode sempre ser um método público (ou interno) de uma classe separada". Ou seja, se um pedaço de lógica é suficientemente avançado para ser sujeito a testes, provavelmente é candidato a ser sua própria classe, com seus próprios testes.
Anders Forsgren,
6
@AndersForsgren Mas o objetivo do teste de unidade , em oposição à integração ou ao teste funcional , é precisamente testar esses detalhes. E a cobertura de código é considerada uma importante métrica de teste de unidade, portanto, em teoria, cada peça de lógica é "suficientemente avançada para estar sujeita a testes".
Kyle Strand
57

Embora eu concorde que o foco do teste de unidade deve ser a interface pública, você obtém uma impressão muito mais granular do seu código se testar também métodos privados. A estrutura de teste da MS permite isso por meio do uso de PrivateObject e PrivateType, o NUnit não. O que eu faço em vez disso é:

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

Dessa forma, você não precisa comprometer o encapsulamento em favor da testabilidade. Lembre-se de que você precisará modificar seus BindingFlags se quiser testar métodos estáticos privados. O exemplo acima é apenas para métodos de instância.

user1039513
fonte
7
O teste desses pequenos métodos auxiliares captura a parte da unidade no teste de unidade. Nem todos são adequados para classes utilitárias.
Johan Larsson
12
Isso é exatamente o que eu precisava. Obrigado! Tudo que eu precisava era verificar se um campo privado estava sendo definido corretamente e NÃO precisei gastar 3 horas para descobrir como determinar isso por meio da interface pública. Este é o código existente que não pode ser refatorado por capricho. É engraçado como uma pergunta simples pode ser respondida com dogmas idealistas ...
Droj
5
Esta deve ser a resposta selecionada, pois é a única que realmente responde à pergunta.
bavaza de
6
Acordado. A resposta escolhida tem seus méritos, mas é a resposta para "devo testar métodos privados", não a pergunta do OP.
chesterbr
2
funciona. Obrigado! Isso é o que muitas pessoas estão procurando. Esta deve ser a resposta selecionada,
Able Johnson
47

Um padrão comum para escrever testes de unidade é testar apenas métodos públicos.

Se você achar que tem muitos métodos privados que deseja testar, normalmente isso é um sinal de que você deve refatorar seu código.

Seria errado tornar esses métodos públicos na classe em que vivem atualmente. Isso quebraria o contrato que você quer que essa classe tenha.

Pode ser correto movê-los para uma classe auxiliar e torná-los públicos lá. Esta classe não pode ser exposta por sua API.

Desta forma, o código de teste nunca é misturado com o seu código público.

Um problema semelhante é testar aulas particulares, ou seja. classes que você não exporta de sua montagem. Nesse caso, você pode tornar seu conjunto de código de teste explicitamente um amigo do conjunto de código de produção usando o atributo InternalsVisibleTo.

morechilli
fonte
1
+ 1 sim! Acabei de chegar a essa conclusão enquanto escrevo meus testes, um bom conselho!
Andy
1
Mover métodos privados que você deseja testar, mas não expor aos usuários da API para uma classe diferente que não está exposta e torná-los públicos, é exatamente o que eu estava procurando.
Thomas N
obrigado @morechilli. Nunca tinha visto InternalsVisibleTo antes. Tornou o teste muito mais fácil.
Andrew MacNaughton
Oi. Recentemente, recebi alguns votos negativos sem comentários. Forneça algum feedback para explicar suas idéias ou preocupações.
morechilli
6
Então, se você tem um método privado que está sendo chamado em vários lugares dentro de uma classe para fazer algo muito específico, necessário apenas para aquela classe e não deve ser exposto como parte da API pública ... você está dizendo para colocá-lo em um auxiliar aula apenas para testar? Você também pode simplesmente torná-lo público para a classe.
Daniel Macias,
21

É possível testar métodos privados declarando seu assembly de teste como um assembly amigo do assembly de destino que você está testando. Consulte o link abaixo para obter detalhes:

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

Isso pode ser útil, pois separa principalmente o código de teste do código de produção. Eu nunca usei esse método porque nunca encontrei a necessidade dele. Suponho que você poderia usá-lo para tentar e testar casos de teste extremos que você simplesmente não pode replicar em seu ambiente de teste para ver como seu código lida com isso.

Como já foi dito, você realmente não precisa testar métodos privados. Você mais do que provavelmente deseja refatorar seu código em blocos de construção menores. Uma dica que pode ajudá-lo ao refatorar é tentar pensar sobre o domínio ao qual seu sistema se relaciona e pensar sobre os objetos 'reais' que habitam esse domínio. Seus objetos / classes em seu sistema devem estar diretamente relacionados a um objeto real, o que permitirá isolar o comportamento exato que o objeto deve conter e também limitar as responsabilidades dos objetos. Isso significa que você está refatorando logicamente, em vez de apenas tornar possível testar um método específico; você poderá testar o comportamento dos objetos.

Se você ainda sente a necessidade de testar internamente, também pode considerar a simulação em seus testes, pois é provável que você queira se concentrar em uma parte do código. Zombar é quando você injeta dependências de objetos, mas os objetos injetados não são os objetos 'reais' ou de produção. Eles são objetos fictícios com comportamento codificado para facilitar o isolamento de erros comportamentais. Rhino.Mocks é um framework de mocking gratuito e popular que essencialmente escreverá os objetos para você. TypeMock.NET (um produto comercial com uma edição da comunidade disponível) é uma estrutura mais poderosa que pode simular objetos CLR. Muito útil para simular as classes SqlConnection / SqlCommand e Datatable, por exemplo, ao testar um aplicativo de banco de dados.

Esperamos que esta resposta forneça um pouco mais de informações para informá-lo sobre os testes de unidade em geral e ajudá-lo a obter melhores resultados com os testes de unidade.

Dafydd Giddins
fonte
12
É possível testar métodos internos , não privados (com NUnit).
TrueWill
6

Sou a favor de ter a capacidade de testar métodos privados. Quando o xUnit foi iniciado, seu objetivo era testar a funcionalidade depois que o código foi escrito. Testar a interface é suficiente para esse propósito.

O teste de unidade evoluiu para o desenvolvimento orientado por teste. Ter a capacidade de testar todos os métodos é útil para esse aplicativo.

Mark Glass
fonte
5

Esta questão está em seus anos avançados, mas pensei em compartilhar minha maneira de fazer isso.

Basicamente, eu tenho todas as minhas classes de teste de unidade no assembly que eles estão testando em um namespace 'UnitTest' abaixo do 'padrão' para esse assembly - cada arquivo de teste é envolvido em um:

#if DEBUG

...test code...

#endif

bloquear, e tudo isso significa que a) não está sendo distribuído em uma versão eb) eu posso usar internal/ Frienddeclarações de nível sem pular de aro.

A outra coisa que isso oferece, mais pertinente a esta questão, é o uso de partialclasses, que podem ser usadas para criar um proxy para testar métodos privados, por exemplo, para testar algo como um método privado que retorna um valor inteiro:

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

nas classes principais do assembly e na classe de teste:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

Obviamente, você precisa garantir que não use esse método durante o desenvolvimento, embora uma compilação de Release indique em breve uma chamada inadvertida para ele se o fizer.

Stuart Wood
fonte
4

O principal objetivo do teste de unidade é testar os métodos públicos de uma classe. Esses métodos públicos usarão esses métodos privados. O teste de unidade testará o comportamento do que está disponível publicamente.

Maxime Rouiller
fonte
3

Pedimos desculpas se isso não responder à pergunta, mas soluções como usar reflexão, instruções #if #endif ou tornar os métodos privados visíveis não resolvem o problema. Pode haver vários motivos para não tornar os métodos privados visíveis ... e se for um código de produção e a equipe estiver escrevendo testes de unidade retrospectivamente, por exemplo.

Para o projeto em que estou trabalhando, apenas o MSTest (infelizmente) parece ter uma maneira, usando acessores, de testar a unidade de métodos privados.

Ritesh
fonte
2

Você não testa funções privadas. Existem maneiras de usar reflexão para entrar em métodos e propriedades privadas. Mas isso não é realmente fácil e eu desencorajo fortemente essa prática.

Você simplesmente não deve testar nada que não seja público.

Se você tiver alguns métodos e propriedades internos, deve considerar alterá-los para públicos ou enviar seus testes com o aplicativo (algo que realmente não vejo como um problema).

Se o seu cliente consegue executar um Test-Suite e vê que o código que você entregou está realmente "funcionando", não vejo isso como um problema (contanto que você não forneça seu IP por meio disso). As coisas que incluo em cada versão são relatórios de teste e relatórios de cobertura de código.

Tigraine
fonte
Alguns clientes tentam manter o orçamento pequeno e iniciam discussões sobre o escopo dos testes. É difícil explicar a essas pessoas que escrever testes não é porque você é um mau programador e não confia em suas próprias habilidades.
MrFox
1

Na teoria do Teste de Unidade, apenas o contrato deve ser testado. ou seja, apenas membros públicos da classe. Mas, na prática, o desenvolvedor geralmente deseja testar membros internos para. - e não é ruim. Sim, vai contra a teoria, mas na prática pode ser útil às vezes.

Então, se você realmente deseja testar membros internos, pode usar uma destas abordagens:

  1. Torne seu membro público. Em muitos livros, os autores sugerem esta abordagem tão simples
  2. Você pode tornar seus membros internos e adicionar InternalVisibleTo ao assebly
  3. Você pode tornar os membros da classe protegidos e herdar sua classe de teste de sua classe em teste.

Exemplo de código (pseudo código):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}
Burzhuy
fonte
Parece que a barata está escondida dentro do seu código de exemplo;) O método dentro da fixação do NUnit deve ser público, caso contrário, você obterá Message: Method is not public.
Gucu112
@ Gucu112 Obrigado. Eu consertei isso. Em geral, isso não importa, pois o objetivo é mostrar uma abordagem do pov de design.
burzhuy
1

Você pode tornar seus métodos protegidos internos e, em seguida, usar assembly: InternalsVisibleTo("NAMESPACE") para seu namespace de teste.

Portanto, NÃO! Você não pode acessar métodos privados, mas esta é uma solução alternativa.

Furgalicious
fonte
0

Eu tornaria o pacote de métodos privados visível. Dessa forma, você o mantém razoavelmente privado enquanto ainda pode testar esses métodos. Não concordo com as pessoas que dizem que as interfaces públicas são as únicas que devem ser testadas. Freqüentemente, há um código realmente crítico nos métodos privados que não podem ser testados adequadamente apenas passando pelas interfaces externas.

Então, realmente se resume a se você se preocupa mais com o código correto ou ocultação de informações. Eu diria que a visibilidade do pacote é um bom compromisso, já que para acessar esses métodos, alguém teria que colocar sua classe em seu pacote. Isso deve realmente fazê-los pensar duas vezes sobre se isso é realmente uma coisa inteligente a se fazer.

Eu sou um cara Java btw, então o pacote visiblilty pode ser chamado de algo totalmente diferente em C #. Basta dizer que é quando duas classes precisam estar no mesmo namespace para acessar esses métodos.

Fylke
fonte