Unidad de prueba de métodos privados en C #

Visual Studio permite la prueba unitaria de métodos privados a través de una clase de acceso generada automáticamente. He escrito una prueba de un método privado que comstack con éxito, pero falla en tiempo de ejecución. Una versión bastante mínima del código y la prueba es:

//in project MyProj class TypeA { private List myList = new List(); private class TypeB { public TypeB() { } } public TypeA() { } private void MyFunc() { //processing of myList that changes state of instance } } //in project TestMyProj public void MyFuncTest() { TypeA_Accessor target = new TypeA_Accessor(); //following line is the one that throws exception target.myList.Add(new TypeA_Accessor.TypeB()); target.MyFunc(); //check changed state of target } 

El error de tiempo de ejecución es:

 Object of type System.Collections.Generic.List`1[MyProj.TypeA.TypeA_Accessor+TypeB]' cannot be converted to type 'System.Collections.Generic.List`1[MyProj.TypeA.TypeA+TypeB]'. 

De acuerdo con intellisense, y supongo que el comstackdor, el objective es de tipo TypeA_Accessor. Pero en el tiempo de ejecución es de tipo TypeA, y por lo tanto, la lista de agregar falla.

¿Hay alguna forma en que pueda detener este error? O, quizás más probable, qué otros consejos tienen otras personas (predigo que tal vez “no pruebes métodos privados” y “no tengo pruebas unitarias que manipulen el estado de los objetos”).

Sí, no pruebes los métodos privados … La idea de una prueba unitaria es probar la unidad con su ‘API’ pública.

Si descubre que necesita probar mucho comportamiento privado, lo más probable es que tenga una nueva ‘clase’ escondida dentro de la clase que está tratando de probar, extráigala y pruébela en su interfaz pública.

Una pieza de consejo / herramienta de pensamiento ….. Existe la idea de que ningún método debe ser privado. Lo que significa que todos los métodos deben vivir en una interfaz pública de un objeto … si cree que necesita hacerlo en privado, lo más probable es que viva en otro objeto.

Este consejo no funciona en la práctica, pero es en su mayoría un buen consejo, y a menudo empujará a las personas a descomponer sus objetos en objetos más pequeños.

Puedes usar PrivateObject Class

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

“No hay nada que se llame como estándar o como mejor práctica, probablemente solo sean opiniones populares”.

Lo mismo es cierto para esta discusión también.

enter image description here

Todo depende de lo que pienses que es una unidad, si crees que UNIT es una clase, entonces solo accederás al método público. Si crees que UNIT es una línea de código que golpea métodos privados, no te hará sentir culpable.

Si desea invocar métodos privados puede usar la clase “PrivateObject” y llamar al método de invocación. Puede ver este video en profundidad de Youtube ( http://www.youtube.com/watch?v=Vq6Gcs9LrPQ ) que muestra cómo usar “PrivateObject” y también analiza si las pruebas de métodos privados son lógicas o no.

Otro pensamiento aquí es extender las pruebas a clases / métodos “internos”, dando más sentido de caja blanca a esta prueba. Puede usar InternalsVisibleToAttribute en el ensamblaje para exponerlos en módulos de prueba de unidades independientes.

En combinación con la clase sellada puede acercarse a tal encapsulación que el método de prueba sea visible solo desde el ensambletesttest de sus métodos. Considere que el método protegido en la clase sellada es de facto privado.

 [assembly: InternalsVisibleTo("MyCode.UnitTests")] namespace MyCode.MyWatch { #pragma warning disable CS0628 //invalid because of InternalsVisibleTo public sealed class MyWatch { Func _getNow = delegate () { return DateTime.Now; }; //construktor for testing purposes where you "can change DateTime.Now" internal protected MyWatch(Func getNow) { _getNow = getNow; } public MyWatch() { } } } 

Y prueba de unidad:

 namespace MyCode.UnitTests { [TestMethod] public void TestminuteChanged() { //watch for traviling in time DateTime baseTime = DateTime.Now; DateTime nowforTesting = baseTime; Func _getNowForTesting = delegate () { return nowforTesting; }; MyWatch myWatch= new MyWatch(_getNowForTesting ); nowforTesting = baseTime.AddMinute(1); //skip minute //TODO check myWatch } [TestMethod] public void TestStabilityOnFebruary29() { Func _getNowForTesting = delegate () { return new DateTime(2024, 2, 29); }; MyWatch myWatch= new MyWatch(_getNowForTesting ); //component does not crash in overlap year } } 

Una forma de probar los métodos privados es a través de la reflexión. Esto se aplica a NUnit y XUnit, también:

 MyObject objUnderTest = new MyObject(); MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance); object[] parameters = {"parameters here"}; methodInfo.Invoke(objUnderTest, parameters); 

Puede crear una clase contenedora alrededor de la clase que contenga el método privado que desea probar. Esta clase contenedora contiene un método público llamado Call_MyPrivateFunction y que a su vez llama a la función privada de su clase base.

Tenga en cuenta que el nivel de acceso del método debería cambiar a [protegido]

Ejemplo de código:

 public class Order { public int Quantity { get; set; } protected bool OrderIsBig () { //This is the method we want to test. It needs to be protected in stead of private. Else... no cigar return Quantity > 100; } } //Use this wrapper class in your unit test. public class FakeOrder : Order { public bool Call_OrderIsBig() { //This makes the actual call to the protected method "OrderIsBig" return OrderIsBig(); } } 

El código de prueba de unidad podría verse así:

 FakeOrder order = new FakeOrder(); order.Quantity = 200; bool isBig = order.Call_OrderIsBig(); //Make a call to a public method of the FakeOrder class which in turn makes a call to the protected method. 

TL; DR: extrae el método privado a otra clase, prueba en esa clase; lea más sobre el principio de SRP (Principio de Responsabilidad Individual)

Parece que necesitas extraer el método private a otra clase; en esto debería ser public . En lugar de tratar de probar en el método private , debe probar public método public de esta otra clase.

Tenemos el siguiente escenario:

 Class A + outputFile: Stream - _someLogic(arg1, arg2) 

Necesitamos probar la lógica de _someLogic ; pero parece que la Class A toma más rol de lo que necesita (viola el principio de SRP); simplemente refactorizar en dos clases

 Class A1 + A1(logicHandler: A2) # take A2 for handle logic + outputFile: Stream Class A2 + someLogic(arg1, arg2) 

De esta forma, someLogic podría ser probado en A2; en A1 simplemente crea un A2 falso e inyecta al constructor para probar que se llama a A2 a la función llamada someLogic .

En VS 2005/2008 puede usar el acceso privado para probar miembros privados, pero de esta manera desapareció en versiones posteriores de VS