Como você testar métodos particulares?

479

Estou construindo uma biblioteca de classes que terá alguns métodos públicos e privados. Eu quero poder testar os métodos privados (principalmente durante o desenvolvimento, mas também pode ser útil para refatoração futura).

Qual é a maneira correta de fazer isso?

Eric Labashosky
fonte
3
Talvez esteja faltando alguma coisa, ou talvez seja apenas essa questão, bem ... pre-historicem termos de anos na Internet, mas o teste de unidade de métodos privados agora é fácil e direto, com o Visual Studio produzindo as classes de acessador necessárias quando necessário e pré-preencher a lógica dos testes com trechos muito próximos do que se pode desejar para testes funcionais simples. Veja por exemplo. msdn.microsoft.com/pt-br/library/ms184807%28VS.90%29.aspx
mjv
3
Parece uma duplicata quase de stackoverflow.com/questions/34571/… .
Raedwald 6/10/11
A pergunta não pode estar usando visual studio
Dave
3
Não faça testes de unidade internals: blog.ploeh.dk/2015/09/22/unit-testing-internals
Mark Seemann

Respostas:

122

Se você estiver usando .net, use o InternalsVisibleToAttribute .

TcKs
fonte
86
Que nojo. Isso é compilado em seus assemblies liberados.
28410 Jay
14
@ Jay - não se poderia usar em #if DEBUGtorno do InternalsVisibleToatributo para torná-lo não se aplica ao código de liberação?
mpontillo
20
@ Mike, você poderia, mas só poderá executar testes de unidade no código de depuração, não no código de liberação. Como o código da versão é otimizado, você pode ver comportamentos diferentes e tempos diferentes. No código multithread, isso significa que seus testes de unidade não detectarão adequadamente as condições da corrida. Muito melhor é usar a reflexão através da sugestão do @ AmazedSaint abaixo ou usar o PrivateObject / PrivateType interno. Isso permite que você veja privates Nas compilações assumindo que o seu equipamento de teste está sendo executado com confiança total (que MSTest executado localmente faz)
Jay
121
o que estou perdendo? Por que essa seria a resposta aceita quando na verdade não responde à pergunta específica de testar métodos privados? InternalsVisibleTo expõe apenas métodos marcados como internos e não aqueles marcados como particulares, conforme solicitado pelo OP (e o motivo pelo qual eu cheguei aqui). Acho que preciso continuar usando o PrivateObject, conforme respondido por Seven?
Darren Lewis
6
@Jay Sei que é um pouco tarde-vindo, mas uma opção é usar algo como #if RELEASE_TESTem torno de InternalsVisibleTocomo Mike sugere, e faça uma cópia de sua configuração de compilação de lançamento que define RELEASE_TEST. Você pode testar seu código de versão com otimizações, mas quando você realmente cria para a versão, seus testes serão omitidos.
Shaz
349

Se você quiser testar um método privado, algo pode estar errado. Os testes de unidade são (geralmente falando) destinados a testar a interface de uma classe, significando seus métodos públicos (e protegidos). É claro que você pode "hackear" uma solução para isso (mesmo que apenas tornando os métodos públicos), mas você também pode querer considerar:

  1. Se o método que você deseja testar realmente vale a pena testar, pode valer a pena movê-lo para sua própria classe.
  2. Adicione mais testes aos métodos públicos que chamam o método privado, testando a funcionalidade do método privado. (Como os comentaristas indicaram, você deve fazer isso apenas se a funcionalidade desses métodos particulares realmente fizer parte da interface pública. Se eles realmente executam funções ocultas do usuário (ou seja, o teste de unidade), isso provavelmente é ruim).
Jeroen Heijmans
fonte
38
A opção 2 faz com que os testes de unidade precisem ter conhecimento da implementação subjacente da função. Eu não gosto de fazer isso. Eu geralmente acho que os testes de unidade devem testar a função sem assumir nada sobre a implementação.
Herms
15
As desvantagens da implementação de teste é que os testes serão frágeis para serem quebrados se você introduzir alguma alteração na implementação. E isso é indesejável, pois a refatoração é tão importante quanto escrever os testes no TDD.
JTR
30
Bem, os testes devem ser interrompidos se você alterar a implementação. TDD significaria mudar os testes primeiro.
sleske
39
@lesleske - eu não concordo. Se a funcionalidade não foi alterada, não há razão para o teste ser interrompido, pois os testes devem realmente estar testando o comportamento / estado, não a implementação. Isso é o que jtr queria dizer sobre tornar seus testes frágeis. Em um mundo ideal, você deve refatorar seu código e ter seus testes ainda aprovados, verificando se sua refatoração não alterou a funcionalidade do seu sistema.
Alconja
26
Desculpas pela ingenuidade (ainda não há muita experiência com testes), mas não é a idéia do teste de unidade para testar todos os módulos de código por conta própria? Eu realmente não entendo por que métodos privados devem ser excluídos dessa idéia.
OMill
118

Pode não ser útil testar métodos privados. No entanto, às vezes também gosto de chamar métodos privados a partir de métodos de teste. Na maioria das vezes, para evitar a duplicação de código para geração de dados de teste ...

A Microsoft fornece dois mecanismos para isso:

Accessors

  • Ir para o código fonte da definição de classe
  • Clique com o botão direito do mouse no nome da turma
  • Escolha "Criar acessador privado"
  • Escolha o projeto no qual o acessador deve ser criado => Você terminará com uma nova classe com o nome foo_accessor. Essa classe será gerada dinamicamente durante a compilação e privará todos os membros disponíveis ao público.

No entanto, o mecanismo às vezes é um pouco intratável quando se trata de alterações na interface da classe original. Então, na maioria das vezes eu evito usar isso.

Classe PrivateObject A outra maneira é usar Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );
Sete
fonte
2
Como você invoca métodos estáticos privados?
StuperUser
18
Os acessadores particulares foram descontinuados no Visual Studio 2012 .
Ryan Gates
3
O método acessador de teste de métodos privados está sendo preterido a partir do VS 2011 em diante. blogs.msdn.com/b/visualstudioalm/archive/2012/03/08/…
Sanjit Misra
1
Lendo os documentos encontrados no site da Microsoft aqui , não vejo nenhuma menção à classe PrivateObject sendo preterida. Estou usando o MSVS 2013 e funciona conforme o esperado.
stackunderflow
3
@RyanGates A primeira solução no site que você mencionou afirma que a solução para acessar membros particulares é "Use a classe PrivateObject para ajudar no acesso a APIs internas e privadas em seu código. Isso pode ser encontrado no Microsoft.VisualStudio.QualityTools.UnitTestFramework. dll assembly ".
stackunderflow
78

Não concordo com a filosofia "você só deveria estar interessado em testar a interface externa". É um pouco como dizer que uma oficina de automóveis só deve ter testes para ver se as rodas giram. Sim, em última análise, estou interessado no comportamento externo, mas eu gosto que meus próprios testes internos sejam um pouco mais específicos e diretos. Sim, se eu refatorar, talvez eu precise alterar alguns dos testes, mas, a menos que seja um refatorio massivo, só precisarei alterar alguns e o fato de os outros testes internos (inalterados) ainda funcionarem é um ótimo indicador de que a refatoração foi bem sucedida.

Você pode tentar cobrir todos os casos internos usando apenas a interface pública e, teoricamente, é possível testar todos os métodos internos (ou pelo menos todos os que importam) inteiramente usando a interface pública, mas você pode ter que ficar de cabeça para alcançar isso e a conexão entre os casos de teste que estão sendo executados pela interface pública e a parte interna da solução projetada para teste podem ser difíceis ou impossíveis de discernir. Tendo apontado, testes individuais que garantem que o maquinário interno esteja funcionando corretamente valem bem as pequenas alterações de teste que ocorrem com a refatoração - pelo menos essa tem sido minha experiência. Se você precisar fazer grandes alterações em seus testes para cada refatoração, talvez isso não faça sentido, mas nesse caso, talvez você deva repensar completamente seu design.

Darrell Plank
fonte
20
Receio ainda discordar de você. Tratar cada componente como uma caixa preta permite que os módulos sejam trocados de entrada / saída sem problemas. Se você tem algo FooServiceque precisa fazer X, tudo o que você deve se preocupar é que ele realmente faz Xquando solicitado. Como isso acontece, não deve importar. Se houver problemas na classe que não sejam discerníveis através da interface (improvável), ainda será válido FooService. Se é um problema que é visível através da interface, um teste em membros públicos deve detectá-lo. O ponto principal deve ser que, desde que a roda gire corretamente, ela possa ser usada como uma roda.
Basic
5
Uma abordagem geral é que, se sua lógica interna é complicada o suficiente para que você exija testes de unidade, talvez ela precise ser extraída para algum tipo de classe auxiliar com uma interface pública que pode ser testada em unidade. Então a turma 'pai' pode simplesmente usar esse auxiliar, e todos podem ser testados adequadamente.
Mir
9
@ Básico: lógica completamente errada nesta resposta. Caso clássico quando você precisa de método privado é quando você precisa que algum código seja reutilizado por métodos públicos. Você coloca esse código em algum PrivMethod. Esse método não deve ser exposto ao público, mas precisa ser testado para garantir que os métodos públicos, que dependem do PrivMethod, possam realmente confiar nele.
Dima
6
@ Dima Certamente, se houver algum problema PrivMethod, um teste no PubMethodqual as chamadas PrivMethoddevem expô-lo? O que acontece quando você muda SimpleSmtpServicepara a GmailService? De repente, seus testes particulares apontam para um código que não existe mais ou que talvez funcione de maneira diferente e que falharia, mesmo que o aplicativo funcione perfeitamente como projetado. Se houver um processamento complexo que se aplicaria a ambos os remetentes de e-mail, talvez ele deva estar em um EmailProcessorque possa ser usado por ambos e testado separadamente?
Basic
2
@miltonb Podemos estar vendo isso de diferentes estilos de desenvolvimento. WRT os internos, eu não tendem a testá-los. Se houver um problema (conforme identificado pelos testes de interface), é fácil rastrear anexando um depurador ou a classe é muito complexa e deve ser dividida (com a interface pública da nova unidade de classes testada) IMHO
Básico
51

Nos raros casos em que eu queria testar funções privadas, geralmente as modifiquei para serem protegidas e escrevi uma subclasse com uma função de invólucro público.

A classe:

...

protected void APrivateFunction()
{
    ...
}

...

Subclasse para teste:

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...
Jason Jackson
fonte
1
Você pode até colocar essa classe filho no seu arquivo de teste de unidade em vez de desordenar a classe real. +1 para astúcia.
quer
Eu sempre coloco todo o código relacionado ao teste no projeto de testes de unidade, se possível. Este foi apenas o código psuedo.
Jason Jackson
2
Esta função é, o seu protegido, o resultado líquido não privada ... você fez o seu código menos seguro / expostas a tipos criança funcionalidade privado
War
22

Eu acho que uma pergunta mais fundamental deve ser feita: por que você está tentando testar o método privado em primeiro lugar? É um cheiro de código que você está tentando testar o método privado através da interface pública dessa classe, enquanto esse método é privado por um motivo, pois é um detalhe de implementação. Deve-se preocupar apenas com o comportamento da interface pública e não com a maneira como ela é implementada nos bastidores.

Se eu quiser testar o comportamento do método privado, usando refatorações comuns, posso extrair seu código para outra classe (talvez com visibilidade no nível do pacote, para garantir que não faça parte de uma API pública). Posso então testar seu comportamento isoladamente.

O produto da refatoração significa que o método privado agora é uma classe separada que se tornou um colaborador da classe original. Seu comportamento será bem compreendido por meio de seus próprios testes de unidade.

Posso então zombar de seu comportamento quando tento testar a classe original, para poder me concentrar em testar o comportamento da interface pública dessa classe, em vez de ter que testar uma explosão combinatória da interface pública e o comportamento de todos os seus métodos privados .

Eu vejo isso como dirigir um carro. Quando dirijo um carro, não dirijo com o capô para cima, para ver que o motor está funcionando. Eu confio na interface que o carro fornece, ou seja, o conta-rotações e o velocímetro para saber que o motor está funcionando. Eu confio no fato de que o carro realmente se move quando eu pressiono o acelerador. Se eu quiser testar o mecanismo, posso verificar isso isoladamente. : D

É claro que testar métodos privados diretamente pode ser o último recurso se você tiver um aplicativo herdado, mas eu preferiria que o código herdado seja refatorado para permitir um teste melhor. Michael Feathers escreveu um ótimo livro sobre esse assunto. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052

Big Kahuna
fonte
16
Lógica completamente errada nesta resposta. Caso clássico quando você precisa de método privado é quando você precisa que algum código seja reutilizado por métodos públicos. Você coloca esse código em algum PrivMethod. Esse método não deve ser exposto ao público, mas precisa ser testado para garantir que os métodos públicos, que dependem do PrivMethod, possam realmente confiar nele.
Dima
Faz sentido durante o desenvolvimento inicial, mas você deseja testes para métodos privados em seu traje de regressão padrão? Nesse caso, se a implementação for alterada, ele poderá interromper o conjunto de testes. OTOH, se seus testes de regressão se concentrarem apenas em métodos públicos visíveis externamente, se o método privado for interrompido posteriormente, o conjunto de regressões ainda deverá detectar o erro. Se necessário, você pode tirar o pó do teste privado antigo, se necessário.
Alex Blakemore
9
Discordo, você deve testar apenas a interface pública, senão por que a necessidade de métodos privados. Torne-os todos públicos nesse caso e teste todos eles. Se você deseja testar um método privado e ele é usado em vários métodos públicos, ele deve ser movido para sua própria classe e testado isoladamente. Todos os métodos públicos devem delegar para essa nova classe. interface da classe original 'e você pode verificar se o comportamento não mudou e se há testes separados para o método privado delegado.
Big Kahuna
@ Big Kahuna - Se você acha que não há nenhum caso em que você precise testar métodos privados, nunca trabalhou com um projeto suficientemente grande / complexo. Muitas vezes uma função pública, como validações específicas do cliente, termina com 20 linhas, chamando métodos privados muito simples para tornar o código mais legível, mas você ainda precisa testar cada método privado individual. Testar 20 vezes a função pública tornará muito difícil iniciar quando os testes de unidade falharem.
Pedro.The.Kid
1
Eu trabalho para uma empresa FTSE 100. Acho que já vi vários projetos complexos no meu tempo, obrigado. Se você precisar testar nesse nível, cada método privado como colaboradores separados deve ser testado isoladamente, porque isso implica que eles têm um comportamento individual que precisa ser testado. O teste para o objeto mediador principal se torna um teste de interação. Apenas teste a estratégia correta que está sendo chamada. Seu cenário parece que a classe em questão não está seguindo o SRP. Não há um único motivo para alterar, mas 20 => violação de SRP. Leia o livro GOOS ou o tio Bob. YMWV
Big Kahuna
17

Tipos particulares, internos e membros particulares são assim por algum motivo, e muitas vezes você não deseja mexer com eles diretamente. E, se o fizer, é provável que você quebre mais tarde, porque não há garantia de que os caras que criaram esses assemblies manterão as implementações privadas / internas como tal.

Mas, às vezes, ao fazer alguns hacks / exploração de montagens compiladas ou de terceiros, acabei desejando inicializar uma classe privada ou uma classe com um construtor privado ou interno. Ou, às vezes, ao lidar com bibliotecas legadas pré-compiladas que não posso alterar - acabo escrevendo alguns testes em um método privado.

Assim nasceu o AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - é uma classe de wrapper rápida que facilita o trabalho usando os recursos dinâmicos e a reflexão do C # 4.0.

Você pode criar tipos internos / privados como

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();
espantado
fonte
12

Bem, você pode testar o método privado de duas maneiras

  1. você pode criar instância da PrivateObjectclasse, a sintaxe é a seguinte

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
  2. Você pode usar reflexão.

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });
Desconhecido
fonte
Boa resposta, mas para # 1 sua sintaxe está errada. Você precisa declarar uma instância PrivateClassprimeiro e usá-la. stackoverflow.com/questions/9122708/…
SharpC
10

Eu também usei o método InternalsVisibleToAttribute. Vale ressaltar também que, se você se sentir desconfortável ao tornar seus métodos anteriormente privados internos para conseguir isso, talvez eles não devam ser submetidos a testes de unidade diretos.

Afinal, você está testando o comportamento de sua classe, em vez de sua implementação específica - você pode alterar o último sem alterar o anterior e seus testes ainda devem passar.

philsquared
fonte
Adoro o ponto de testar o comportamento, e não a implementação. Se você vincular seus testes de unidade à implementação (métodos privados), os testes se tornarão frágeis e precisarão mudar quando a implementação for alterada.
Bob Horn
9

Existem 2 tipos de métodos privados. Métodos privados estáticos e métodos privados não estáticos (métodos de instância). Os 2 artigos a seguir explicam como testar métodos particulares com exemplos.

  1. Métodos particulares estáticos de teste de unidade
  2. Métodos particulares não estáticos de teste de unidade
Venkat
fonte
Dar alguns exemplos, não só dar um link
vinculis
Parece feio. Sem inteligência. Solução ruim do MS. Estou em choque!
Alerya
Maneira mais fácil de testar métodos estáticos privados
Grant
8

O MS Test possui um bom recurso interno que disponibiliza membros e métodos privados no projeto, criando um arquivo chamado VSCodeGenAccessors

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

Com classes derivadas de BaseAccessor

tal como

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }
Marcus King
fonte
8
Os arquivos gerados existem apenas no VS2005. Em 2008 eles são gerados nos bastidores. E eles são uma abmominação. E a tarefa Shadow associada é escassa em um servidor de construção.
Ruben Bartelink
Os acessadores também foram descontinuados no VS2012-2013.
Zephan Schroeder 01/03/19
5

No CodeProject, há um artigo que discute brevemente os prós e contras do teste de métodos particulares. Em seguida, fornece algum código de reflexão para acessar métodos privados (semelhante ao código que Marcus fornece acima.) O único problema que encontrei com a amostra é que o código não leva em consideração os métodos sobrecarregados.

Você pode encontrar o artigo aqui:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

Pedro
fonte
4

Declare-os internale use o InternalsVisibleToAttributepara permitir que seu conjunto de teste da unidade os veja.

James Curran
fonte
11
Não gosto de usar InternalsVisibleTo porque tornei o método privado por um motivo.
30510 swilliams
4

Eu costumo não usar diretivas do compilador porque elas bagunçam as coisas rapidamente. Uma maneira de atenuá-lo se você realmente precisar deles é colocá-los em uma classe parcial e fazer com que sua construção ignore esse arquivo .cs ao criar a versão de produção.

Swilliams
fonte
Você incluiria os acessadores de teste em uma versão de produção (para testar otimizações do compilador etc.), mas os excluiria em uma versão de lançamento. Mas estou cortando cabelos e votei de qualquer maneira porque acredito que é uma boa ideia colocar essas coisas em um único lugar. Obrigado pela ideia.
CAD cara
4

Você não deve testar os métodos particulares do seu código em primeiro lugar. Você deve testar a 'interface pública' ou API, o conteúdo público de suas classes. A API são todos os métodos públicos que você expõe a chamadores externos.

O motivo é que, assim que você começa a testar os métodos privados e internos da sua classe, está acoplando a implementação da sua classe (as coisas particulares) aos seus testes. Isso significa que, quando você decide alterar os detalhes de sua implementação, também precisará alterar seus testes.

Por esse motivo, evite usar InternalsVisibleToAtrribute.

Aqui está uma ótima conversa de Ian Cooper, que aborda este assunto: Ian Cooper: TDD, onde tudo deu errado

cda01
fonte
3

Às vezes, pode ser bom testar declarações privadas. Fundamentalmente, um compilador possui apenas um método público: Compile (string outputFileName, params string [] sourceSFileNames). Tenho certeza que você entende que seria difícil testar esse método sem testar cada declaração "oculta"!

Por isso, criamos o Visual T #: para facilitar os testes. É uma linguagem de programação .NET gratuita (compatível com C # v2.0).

Nós adicionamos o operador '.-'. Apenas se comporta como '.' operador, exceto que você também pode acessar qualquer declaração oculta de seus testes sem alterar nada em seu projeto testado.

Dê uma olhada no nosso site: baixar -lo gratuitamente .

Ludovic Dubois
fonte
3

Estou surpreso que ninguém tenha dito isso ainda, mas uma solução que empreguei é criar um método estático dentro da classe para testar a si mesmo. Isso lhe dá acesso a tudo o que é público e privado para testar.

Além disso, em uma linguagem de script (com recursos de OO, como Python, Ruby e PHP), você pode fazer o teste do arquivo quando executar. Ótima maneira rápida de garantir que suas alterações não quebrem nada. Obviamente, isso cria uma solução escalável para testar todas as suas classes: basta executá-las. (você também pode fazer isso em outros idiomas com um void main que sempre executa seus testes também).

rube
fonte
1
Embora isso seja prático, não é muito elegante. Isso pode criar uma confusão de uma base de código e também não permite que você separe seus testes do seu código real. A capacidade de testar externamente abre a capacidade de automatizar scripts em vez de escrever métodos estáticos manualmente.
Darren Reid
Isso não impede você de testar externamente ... basta chamar o método estático como quiser. A base de código também não é confusa ... você nomeia o método de acordo. Eu uso "runTests", mas qualquer coisa semelhante funciona.
rube
Você tem razão, isso não impede testes externos, mas gera muito mais código, ou seja, torna a base de código confusa. Cada classe pode ter muitos métodos particulares para testar o que inicializa suas variáveis ​​em um ou mais de seus construtores. Para testar, você precisará escrever pelo menos tantos métodos estáticos quanto os métodos a serem testados, e os métodos de teste podem ter que ser grandes para inicializar os valores corretos. Isso tornaria a manutenção do código mais difícil. Como outros já disseram, testar o comportamento de uma classe é uma abordagem melhor; o restante deve ser pequeno o suficiente para depurar.
Darren Reid
Eu uso o mesmo número de linhas para testar como qualquer outra pessoa (na verdade, menos do que você lerá mais tarde). Você não precisa testar TODOS os seus métodos particulares. Apenas aqueles que precisam de teste :) Você também não precisa testar cada um em um método separado. Eu faço isso com uma ligação. Na verdade, isso dificulta a manutenção do código LESS, pois todas as minhas classes têm o mesmo método de teste de unidade guarda-chuva, que executa todos os testes de unidade privados e protegidos linha por linha. Todo o equipamento de teste chama o mesmo método em todas as minhas classes e a manutenção reside na minha classe - testes e tudo.
rube
3

Quero criar um exemplo de código claro aqui, que você pode usar em qualquer classe na qual deseja testar o método privado.

Na sua classe de caso de teste, inclua esses métodos e depois os empregue conforme indicado.

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ this -> _ callMethod ('_ someFunctionName', array (param1, param2, param3));

Basta emitir os parâmetros na ordem em que aparecem na função privada original

Damon Hogan
fonte
3

Para quem quer executar métodos privados sem todo o estresse e confusão. Isso funciona com qualquer estrutura de teste de unidade usando nada além do bom e velho Reflection.

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

Em seus testes reais, você pode fazer algo assim:

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");
Erick Stone
fonte
2

O MbUnit conseguiu um bom invólucro para este chamado Refletor.

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

Você também pode definir e obter valores das propriedades

dogReflector.GetProperty("Age");

Em relação ao "teste privado", eu concordo que .. no mundo perfeito. não faz sentido fazer testes de unidade privada. Mas no mundo real, você pode querer escrever testes particulares em vez de refatorar o código.

Carl Bergquist
fonte
4
Apenas para informação, Reflectorfoi substituído pelo mais poderoso Mirrordo Gallio / MbUnit v3.2. ( gallio.org/wiki/doku.php?id=mbunit:mirror )
Yann Trevin
2

Aqui está um bom artigo sobre teste de unidade de métodos privados. Mas não tenho certeza do que é melhor: tornar seu aplicativo projetado especialmente para testes (é como criar testes apenas para testes) ou usar a reflexão para testes. Certamente a maioria de nós escolherá o segundo caminho.

Johnny_D
fonte
2

Na minha opinião, você deve testar apenas a API pública da sua classe.

Tornar um método público, para testá-lo por unidade, quebra o encapsulamento, expondo os detalhes da implementação.

Uma boa API pública resolve um objetivo imediato do código do cliente e resolve esse objetivo completamente.

jpchauny
fonte
Esta deve ser a resposta certa IMO. Se você possui muitos métodos particulares, provavelmente é porque você tem uma classe oculta que deve ser quebrada em sua própria interface pública.
sunefred
2

Eu uso a classe PrivateObject . Mas, como mencionado anteriormente, é melhor evitar testar métodos privados.

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);
vsapiha
fonte
2
CC -Dprivate=public

"CC" é o compilador de linha de comando no sistema que eu uso. -Dfoo=barfaz o equivalente a #define foo bar. Portanto, essa opção de compilação efetivamente altera todas as coisas privadas para públicas.

Mark Harrison
fonte
2
O que é isso? Isso se aplica ao Visual Studio?
YeahStu
"CC" é o compilador de linha de comando no sistema que eu uso. "-Dfoo = bar" faz o equivalente a "#define foo bar". Portanto, essa opção de compilação efetivamente altera todas as coisas privadas para públicas. ha-ha!
23415 Mark Harrison
No Visual Studio, defina uma definição em seu ambiente de compilação.
Mark Harrison
1

Aqui está um exemplo, primeiro a assinatura do método:

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

Aqui está o teste:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}
Chuck Savage
fonte
1

Uma maneira de fazer isso é ter seu método protectede escrever um acessório de teste que herda sua classe a ser testada. Dessa forma, você não está mudando seu método public, mas habilita o teste.

Kiriloff
fonte
Não concordo com este, porque você também permitirá que seus consumidores herdem da classe base e usem as funções protegidas. Isso era algo que você queria evitar em primeiro lugar, tornando essas funções privadas ou internas.
21815 Nick Nick
1

1) Se você possui um código legado, a única maneira de testar métodos privados é por reflexão.

2) Se for um novo código, você terá as seguintes opções:

  • Use reflexão (para complicado)
  • Escreva teste de unidade na mesma classe (torna o código de produção feio, tendo também o código de teste)
  • Refatore e torne o método público em algum tipo de classe util
  • Use a anotação @VisibleForTesting e remova os dados privados

Prefiro o método de anotação, mais simples e menos complicado. A única questão é que aumentamos a visibilidade, o que eu acho que não é uma grande preocupação. Devemos sempre codificar a interface, portanto, se tivermos uma interface MyService e uma implementação MyServiceImpl, poderemos ter as classes de teste correspondentes que são MyServiceTest (métodos de interface de teste) e MyServiceImplTest (métodos privados de teste). Todos os clientes devem, de qualquer maneira, estar usando a interface, de modo que, mesmo que a visibilidade do método privado tenha aumentado, isso realmente não deve importar.

anuj
fonte
1

Você também pode declarar como público ou interno (com InternalsVisibleToAttribute) ao criar no modo de depuração:

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

Ele incha o código, mas estará privateem uma versão compilada.

Alex H
fonte
0

Você pode gerar o método de teste para o método privado no Visual studio 2008. Quando você cria um teste de unidade para um método privado, uma pasta Referências de Teste é adicionada ao seu projeto de teste e um acessador é adicionado a essa pasta. O acessador também é referido na lógica do método de teste de unidade. Esse acessador permite que seu teste de unidade chame métodos particulares no código que você está testando. Para mais detalhes, consulte

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

Sarath
fonte
0

Observe também que o InternalsVisibleToAtrribute tem um requisito de que seu assembly tenha um nome forte , o que cria seu próprio conjunto de problemas se você estiver trabalhando em uma solução que não tinha esse requisito antes. Eu uso o acessador para testar métodos privados. Veja esta pergunta que, para um exemplo disso.

Chuck Dee
fonte
2
Não, o InternalsVisibleToAttributeque não exigem que suas montagens ser fortemente nomeado. Atualmente, eu o uso em um projeto em que esse não é o caso.
Cody Gray
1
Para esclarecer isso: "Tanto a assembléia atual quanto a assembléia de amigos devem estar sem sinal ou ambas devem ser assinadas com um nome forte." - Do MSDN
Hari