Recientemente estuve discutiendo con algunos amigos cuál de los siguientes 2 métodos es el mejor para resguardar los resultados devueltos o las llamadas a métodos dentro de la misma clase de los métodos dentro de la misma clase.
Este es un ejemplo muy simplificado. En realidad, las funciones son mucho más complejas.
Ejemplo:
public class MyClass
{
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected int FunctionB()
{
return new Random().Next();
}
}
Entonces, para probar esto, tenemos 2 métodos.
Método 1: utilice funciones y acciones para reemplazar la funcionalidad de los métodos. Ejemplo:
public class MyClass
{
public Func<int> FunctionB { get; set; }
public MyClass()
{
FunctionB = FunctionBImpl;
}
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected int FunctionBImpl()
{
return new Random().Next();
}
}
[TestClass]
public class MyClassTests
{
private MyClass _subject;
[TestInitialize]
public void Initialize()
{
_subject = new MyClass();
}
[TestMethod]
public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
{
_subject.FunctionB = () => 1;
var result = _subject.FunctionA();
Assert.IsFalse(result);
}
}
Método 2: Haga que los miembros sean virtuales, la clase derivada y en la clase derivada use Funciones y Acciones para reemplazar la funcionalidad Ejemplo:
public class MyClass
{
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected virtual int FunctionB()
{
return new Random().Next();
}
}
public class TestableMyClass
{
public Func<int> FunctionBFunc { get; set; }
public MyClass()
{
FunctionBFunc = base.FunctionB;
}
protected override int FunctionB()
{
return FunctionBFunc();
}
}
[TestClass]
public class MyClassTests
{
private TestableMyClass _subject;
[TestInitialize]
public void Initialize()
{
_subject = new TestableMyClass();
}
[TestMethod]
public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
{
_subject.FunctionBFunc = () => 1;
var result = _subject.FunctionA();
Assert.IsFalse(result);
}
}
Quiero saber cuál es mejor y también ¿POR QUÉ?
Actualización: NOTA: La función B también puede ser pública
c#
design
unit-testing
tranceru1
fuente
fuente
FunctionA
devuelve un bool pero solo establece una variable localx
y no devuelve nada.public static
pero en una clase diferente.FunctionB
está roto por diseño.new Random().Next()
Casi siempre está mal. Debe inyectar la instancia deRandom
. (Random
también es una clase mal diseñada, que puede causar algunos problemas adicionales)Respuestas:
Editado después de la actualización original del póster.
Descargo de responsabilidad: no es un programador de C # (principalmente Java o Ruby). Mi respuesta sería: no lo probaría en absoluto, y no creo que debas hacerlo.
La versión más larga es: los métodos privados / protegidos no son parte de la API, son básicamente opciones de implementación, que puede decidir revisar, actualizar o descartar por completo sin ningún impacto en el exterior.
Supongo que tiene una prueba en FunctionA (), que es la parte de la clase que es visible desde el mundo externo. Debería ser el único que tiene un contrato para implementar (y que podría probarse). Su método privado / protegido no tiene contrato para cumplir y / o probar.
Vea una discusión relacionada allí: https://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones
Después del comentario , si FunctionB es público, simplemente probaré ambos utilizando la prueba unitaria. Puede pensar que la prueba de FunctionA no es totalmente "unidad" (como se llama FunctionB), pero eso no me preocuparía demasiado: si la prueba de FunctionB funciona pero no la prueba de FunctionA, significa claramente que el problema no está en el subdominio de FunctionB, que es lo suficientemente bueno para mí como discriminador.
Si realmente desea poder separar por completo las dos pruebas, usaría algún tipo de técnica de burla para burlarse de FunctionB cuando pruebe FunctionA (generalmente, devuelve un valor correcto conocido y fijo). Me falta el conocimiento del ecosistema C # para aconsejar una biblioteca de burlas específica, pero puede consultar esta pregunta .
fuente
MyClass
y anular el método con la funcionalidad que desea stub. También podría ser una buena idea actualizar su pregunta para incluirla queFunctionB
podría ser pública.protected
Los métodos son parte de la superficie pública de una clase, a menos que se asegure de que no puede haber implementaciones de su clase en diferentes ensamblados.Me suscribo a la teoría de que si una función es importante para probar, o es importante para reemplazar, es lo suficientemente importante como para no ser un detalle de implementación privada de la clase bajo prueba, sino ser un detalle de implementación pública de una clase diferente .
Entonces, si estoy en un escenario donde tengo
Entonces voy a refactorizar.
Ahora tengo un escenario en el que D () es independientemente comprobable y completamente reemplazable.
Como medio de organización, mi colaborador podría no vivir en el mismo nivel de espacio de nombres. Por ejemplo, si
A
está en FooCorp.BLL, entonces mi colaborador podría tener otra capa más profunda, como en FooCorp.BLL.Collaborators (o cualquier nombre que sea apropiado). Es posible que mi colaborador solo sea visible dentro del ensamblaje a través delinternal
modificador de acceso, que luego expondría a mis proyectos de prueba de unidad a través delInternalsVisibleTo
atributo de ensamblaje. La conclusión es que aún puede mantener su API limpia, en lo que respecta a las personas que llaman, mientras produce un código verificable.fuente
Agregando a lo que Martin señala,
Si su método es privado / protegido, no lo pruebe. Es interno de la clase y no se debe acceder fuera de la clase.
En ambos enfoques que mencionas, tengo estas preocupaciones:
Método 1: esto realmente cambia el comportamiento de la clase bajo prueba en la prueba.
Método 2: en realidad, esto no prueba el código de producción, sino que prueba otra implementación.
En el problema indicado, veo que la única lógica de A es ver si la salida de FunctionB es par. Aunque es ilustrativo, FunctionB proporciona un valor aleatorio, que es difícil de probar.
Espero un escenario realista en el que podamos configurar MyClass de modo que sepamos qué FunctionB devolvería. Entonces se conoce nuestro resultado esperado, podemos llamar a FunctionA y afirmar el resultado real.
fuente
protected
es casi lo mismo quepublic
. Soloprivate
yinternal
son detalles de implementación.internal protected
, usar un asistente de reflexión privado o crear una clase derivada en su proyecto de prueba.Personalmente uso el Método 1, es decir, convertir todos los métodos en Acciones o Funciones, ya que esto me ha mejorado enormemente la capacidad de prueba del código. Como con cualquier solución, hay ventajas y desventajas con este enfoque:
Pros
Contras
En resumen, usar Funcs and Actions for Unit test es excelente si sabes que tus clases nunca serán anuladas.
Además, generalmente no creo propiedades para Funcs, sino que las alineo directamente como tal
¡Espero que esto ayude!
fuente
Usando Mock es posible. Nuget: https://www.nuget.org/packages/moq/
Y créeme, es bastante simple y tiene sentido.
Mock necesita un método virtual para anular.
fuente