Como você testa methods privados?

Estou construindo uma biblioteca de classs que terá alguns methods públicos e privados. Eu quero ser capaz de testar a unidade dos methods privados (principalmente durante o desenvolvimento, mas também pode ser útil para refatoração futura).

Qual é a maneira correta de fazer isso?

Se você estiver usando .net, você deve usar o InternalsVisibleToAttribute .

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

  1. Se o método que você gostaria de testar realmente vale a pena ser testado, pode valer a pena movê-lo para sua própria class.
  2. Adicione mais testes aos methods 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 methods privados for realmente parte da interface pública. Se eles realmente executarem funções que estão ocultas do usuário (ou seja, o teste de unidade), isso provavelmente é ruim).

Pode não ser útil testar methods privados. No entanto, às vezes também gosto de chamar methods privados de methods de teste. Na maior parte do tempo, para evitar 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 da class
  • Clique com o botão direito do mouse no nome da class
  • Escolha “Criar Acessor Privado”
  • Escolha o projeto em que o acessador deve ser criado => Você vai acabar com uma nova class com o nome foo_accessor. Esta class será gerada dinamicamente durante a compilation e privará todos os membros públicos disponíveis.

No entanto, o mecanismo às vezes é um pouco intratável quando se trata de alterações da interface da class 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[] {/* ... */} ); 

Eu não concordo com a filosofia “você deveria estar interessado apenas em testar a interface externa”. É um pouco como dizer que uma oficina de carros só deveria ter testes para ver se as rodas giravam. Sim, em última análise, estou interessado no comportamento externo, mas gosto que os meus próprios testes privados internos sejam um pouco mais específicos e objectives. Sim, se eu refatorar, posso ter que alterar alguns dos testes, mas a menos que seja um refatorador em massa, terei apenas que mudar alguns e o fato de que os outros testes internos (inalterados) ainda funcionam é 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 todo método interno (ou pelo menos todos que importam) inteiramente usando a interface pública, mas você pode ter que acabar ficando na sua 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 que eles projetam para testar pode ser difícil ou impossível de discernir. Tendo apontado, testes individuais que garantem que o maquinário interno está funcionando adequadamente valem bem as pequenas mudanças de teste que surgem com a refatoração – pelo menos essa tem sido minha experiência. Se você tiver que fazer grandes mudanças nos seus testes para cada refatoração, então talvez isso não faça sentido, mas, nesse caso, talvez você deva repensar seu design completamente. Um bom design deve ser flexível o suficiente para permitir a maioria das alterações sem grandes reformulações.

Nos casos raros, eu queria testar funções privadas, em geral modifiquei-as para serem protegidas, e escrevi uma subclass com uma function public wrapper.

A class:

 ... protected void APrivateFunction() { ... } ... 

Subclass para teste:

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

Eu acho que uma pergunta mais fundamental deveria ser perguntada: por que você está tentando testar o método privado em primeiro lugar? Isso é um cheiro de código que você está tentando testar o método privado através da interface pública dessa class, enquanto que o método é privado por uma razão, já que é um detalhe de implementação. Deve-se preocupar apenas com o comportamento da interface pública, não em como ela é implementada nos bastidores.

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

O produto da refatoração significa que o método privado é agora uma class separada que se tornou um colaborador para a class original. Seu comportamento terá se tornado bem compreendido através de seus próprios testes unitários.

Eu posso então zombar de seu comportamento quando tento testar a class original para que eu possa concentrar-me em testar o comportamento da interface pública dessa class em vez de ter que testar uma explosão combinatória da interface pública e o comportamento de todos os seus methods privados .

Eu vejo isso análogo a dirigir um carro. Quando eu dirijo um carro eu 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, no conta-rotações e no velocímetro para saber que o motor está funcionando. Eu confio no fato de que o carro realmente se move quando eu aperto o pedal do acelerador. Se eu quiser testar o motor, posso verificar isso isoladamente. : D

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

Tipos privados, internos e membros privados são assim por causa de alguma razão, e muitas vezes você não quer mexer com eles diretamente. E se você fizer isso, é 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 assemblies compilados ou de terceiros, eu mesmo acabei querendo inicializar uma class privada ou uma class 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 class de wrapper rápida que facilitará o trabalho usando resources dynamics e reflection 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(); 

Eu também usei o método InternalsVisibleToAttribute. Vale a pena mencionar também que, se você se sentir desconfortável em tornar seus methods previamente privados internos para conseguir isso, então talvez eles não devam ser sujeitos a testes unitários diretos de qualquer maneira.

Afinal de contas, você está testando o comportamento de sua class, em vez de sua implementação específica – você pode alterar a última sem alterar a primeira e seus testes ainda devem passar.

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

  1. você pode criar uma instância da class PrivateObject a syntax é 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 reflection.

     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 }); 

O MS Test possui um belo recurso integrado que disponibiliza membros e methods 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 classs que derivam 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; } 

Existem 2 tipos de methods privados. Métodos Particulares Estáticos e Métodos Particulares Não Estáticos (Métodos de Instância). Os 2 artigos seguintes explicam como testar methods privados com exemplos.

  1. Métodos privados estáticos de teste de unidade
  2. Testes Unitários Métodos Particulares Não Estáticos

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

No CodeProject, há um artigo que discute brevemente os prós e contras de testar methods privados. Em seguida, ele fornece algum código de reflection para acessar methods privados (semelhante ao código fornecido pelo Marcus acima.) O único problema encontrado com a amostra é que o código não leva em consideração methods sobrecarregados.

Você pode encontrar o artigo aqui:

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

Declará-los internal e, em seguida, use o InternalsVisibleToAttribute para permitir que seu assembly de teste de unidade os veja.

Às vezes, pode ser bom testar declarações privadas. Fundamentalmente, um compilador tem 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 que criamos o Visual T #: para facilitar os testes. É uma linguagem de programação .NET livre (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: baixe gratuitamente .

Você não deveria estar testando os methods privados do seu código em primeiro lugar. Você deveria estar testando a ‘interface pública’ ou API, as coisas públicas de suas classs. A API são todos os methods públicos que você expõe a chamadores externos.

A razão é que, uma vez que você começa a testar os methods privados e internos de sua class, você está acoplando a implementação de sua class (as coisas privadas) aos seus testes. Isso significa que, quando você decidir alterar seus detalhes de implementação, também precisará alterar seus testes.

Você deve por esse motivo evitar o uso de InternalsVisibleToAtrribute.

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

MbUnit tem um bom wrapper para este chamado Reflector.

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

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

 dogReflector.GetProperty("Age"); 

Em relação ao “teste privado” eu concordo que .. no mundo perfeito. não faz sentido fazer testes unitários privados. Mas no mundo real, você pode acabar querendo escrever testes privados em vez de refatorar o código.

Estou surpreso que ninguém tenha dito isso ainda, mas uma solução que tenho empregado é fazer um método estático dentro da class para testar a si mesmo. Isto dá-lhe access a tudo o que é público e privado para testar.

Além disso, em uma linguagem de script (com habilidades OO, como Python, Ruby e PHP), você pode fazer o teste do arquivo quando executado. Bom modo rápido de ter certeza que suas mudanças não quebraram nada. Isso obviamente faz uma solução escalável para testar todas as suas classs: apenas execute todas elas. (você também pode fazer isso em outras linguagens com um void main que sempre executa seus testes também).

Eu quero criar um exemplo de código claro aqui que você pode usar em qualquer class em que você deseja testar o método privado.

Em sua class de caso de teste, inclua esses methods e, em seguida, use-os 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 function privada original

Para quem quer executar methods privados sem todos os confessos e bagunça. 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; } } 

Then in your actual tests, you can do something like this:

 Assert.AreEqual( ReflectionTools.InvokePrivate( typeof(StaticClassOfMethod), "PrivateMethod"), "Expected Result"); Assert.AreEqual( ReflectionTools.InvokePrivate( new ClassOfMethod(), "PrivateMethod"), "Expected Result"); 
 CC -Dprivate=public 

Here is good article about unit testing of private methods. But I’m not sure what’s better, to make you application designed specially for testing(it’s like creating tests for testing only) or use reflexion for testing. Pretty sure most of us will choose second way.

I use PrivateObject class. But as mentioned previously better to avoid testing private methods.

 Class target = new Class(); PrivateObject obj = new PrivateObject(target); var retVal = obj.Invoke("PrivateMethod"); Assert.AreEqual(retVal); 

You could generate the test method for the private method from Visual studio 2008. When you create a unit test for a private method, a Test References folder is added to your test project and an accessor is added to that folder. The accessor is also referred to in the logic of the unit test method. This accessor allows your unit test to call private methods in the code that you are testing. For details have a look at

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

Also note that the InternalsVisibleToAtrribute has a requirement that your assembly be strong named , which creates it’s own set of problems if you’re working in a solution that had not had that requirement before. I use the accessor to test private methods. See this question that for an example of this.

Here’s an example, first the method signature:

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

Here’s the test:

 ///  ///A test for SplitInternal /// [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); } 

A way to do this is to have your method protected and write a test fixture which inherits your class to be tested. This way, you are nor turning your method public , but you enable the testing.

1) If you have a legacy code then the only way to test private methods is by reflection.

2) If it is new code then you have the following options:

  • Use reflection (to complicated)
  • Write unit test in the same class (makes the production code ugly by having test code also in it)
  • Refactor and make the method public in some kind of util class
  • Use @VisibleForTesting annotation and remove private

I prefer the annotation method, simplest and least complicated. The only issue is that we have increased the visibility which I think is not a big concern. We should always be coding to interface, so if we have an interface MyService and an implementation MyServiceImpl then we can have the corresponding test classs that is MyServiceTest (test interface methods) and MyServiceImplTest (test private methods). All clients should anyway be using the interface so in a way even though the visibility of the private method has been increased it should not really matter.

You could also declare it as public or internal (with InternalsVisibleToAttribute) while building in debug-Mode:

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

It bloats the code, but it will be private in a release build.