mandar - metodos y propiedades en c#
Unidad de pruebas de métodos privados en C#. (8)
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 compila con éxito, pero falla en el tiempo de ejecución. Una versión bastante mínima del código y la prueba es:
//in project MyProj
class TypeA
{
private List<TypeB> myList = new List<TypeB>();
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]''.
Según intellisense, y por lo tanto supongo que el objetivo del compilador 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 manera de que pueda detener este error? O, quizás más probable, qué otros consejos tienen otras personas (predigo que tal vez "no pruebe métodos privados" y "no haga que las pruebas unitarias manipulen el estado de los objetos").
"No hay nada que se llame estándar o como mejor práctica, probablemente solo sean opiniones populares".
Lo mismo se aplica a esta discusión también.
Todo depende de lo que creas que es una unidad, si crees que UNIT es una clase, solo alcanzarás el método público. Si crees que UNIT es una línea de código que utiliza métodos privados no te hará sentir culpable.
Si desea invocar métodos privados, puede usar la clase "PrivateObject" y llamar al método invoke. Puede ver este video en profundidad de YouTube ( http://www.youtube.com/watch?v=Vq6Gcs9LrPQ ) que muestra cómo usar "PrivateObject" y también explica si las pruebas de los métodos privados son lógicas o no.
En VS 2005/2008 puede usar el acceso privado para probar miembros privados, pero de esta manera desapareció en la versión posterior de VS
Otra idea aquí es extender las pruebas a clases / métodos "internos", dando más de un sentido de caja blanca de estas pruebas. Puede usar InternalsVisibleToAttribute en el ensamblaje para exponerlos a módulos de prueba de unidad separados.
En combinación con la clase sellada, puede aproximarse a tal encapsulación de que el método de prueba solo es visible desde los métodos de ensamblaje de unittest. Considere que el método protegido en 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<DateTime> _getNow = delegate () { return DateTime.Now; };
//construktor for testing purposes where you "can change DateTime.Now"
internal protected MyWatch(Func<DateTime> 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<DateTime> _getNowForTesting = delegate () { return nowforTesting; };
MyWatch myWatch= new MyWatch(_getNowForTesting );
nowforTesting = baseTime.AddMinute(1); //skip minute
//TODO check myWatch
}
[TestMethod]
public void TestStabilityOnFebruary29()
{
Func<DateTime> _getNowForTesting = delegate () { return new DateTime(2024, 2, 29); };
MyWatch myWatch= new MyWatch(_getNowForTesting );
//component does not crash in overlap year
}
}
Puede crear una clase contenedora alrededor de la clase que contiene 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 la unidad podría verse como:
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.
Puedes usar la clase PrivateObject
Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal, expectedVal);
Sí, no pruebe métodos privados ... La idea de una prueba de unidad es probar la unidad por su ''API'' pública.
Si encuentra que necesita probar una gran cantidad de comportamientos privados, lo más probable es que tenga una nueva ''clase'' que se oculte dentro de la clase que está intentando probar, extraerla y probarla por su interfaz pública.
Un 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 sientes que necesitas hacerlo privado, lo más probable es que viva en otro objeto.
Este consejo no funciona bien en la práctica, pero es en su mayoría un buen consejo, y con frecuencia empujará a las personas a descomponer sus objetos en objetos más pequeños.
Una forma de probar métodos privados es a través de la reflexión. Esto también se aplica a NUnit y XUnit:
MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);
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 única)
Parece que necesitas extraer el método private
a otra clase; En esto debería ser public
. En lugar de intentar probar 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 del que necesita (viola el principio de SRP); solo refactorizar en dos clases
Class A1
+ A1(logicHandler: A2) # take A2 for handle logic
+ outputFile: Stream
Class A2
+ someLogic(arg1, arg2)
De esta manera, algunos someLogic
podrían probarse en A2; en A1 simplemente cree algo de A2 falso e inyecte al constructor para probar que A2 se llama a la función llamada someLogic
.