Pregunta

Estoy escribiendo una serie de clases de recolección en C#, cada una de las cuales implementa interfaces personalizadas similares. ¿Es posible escribir una sola colección de pruebas unitarias para una interfaz y ejecutarlas automáticamente todas en varias implementaciones diferentes? Me gustaría evitar cualquier código de prueba duplicado para cada implementación.

Estoy dispuesto a investigar cualquier marco (NUNIT, etc.) o la extensión de Visual Studio para lograr esto.


Para aquellos que buscan hacer lo mismo, publiqué mi solución concreta, basada en La solución aceptada de Avandeursen, como una respuesta.

¿Fue útil?

Solución

Sí, eso es posible. El truco es dejar que la jerarquía de pruebas de clase de su unidad siga la jerarquía de clases de su código.

Supongamos que tiene una interfaz Itf con la implementación de clases C1 y C2.

Primero crea una clase de prueba para Itf (ItfTest). Para ejercer realmente la prueba, debe crear una implementación simulada de su Itf interfaz.

Todas las pruebas en esto ItfTest debe transmitir cualquier implementación de Itf (!). Si no, su implementación no se ajusta al Principio de sustitución de Liskov (la "L" en Martin's SÓLIDO Principios del diseño OO)

Por lo tanto, para crear un caso de prueba para C1, su C1Test la clase puede extenderse ItfTest. Su extensión debe reemplazar la creación de objetos simulados con la creación de un C1 objeto (inyectarlo o usar un Método de fábrica de GoF). De esta manera, todos ItfTest Los casos se aplican a instancias de tipo C1. Además, tu C1Test la clase puede contener casos de prueba adicionales específicos para C1.

Igualmente para C2. Y puedes repetir el truco para clases e interfaces anidadas más profundas.

Referencias: Binder's Prueba de servidor polimórfico patrón y McGregor's PACTO - Arquitectura paralela para pruebas de componentes.

Otros consejos

Expandir Joe's Respuesta, puede usar el atributo [testCasesurce] en NUNIT de manera similar a la prueba de mBUNIT. Puede crear una fuente de caso de prueba con sus nombres de clase allí. Luego puede decorar cada prueba que el atributo TestCasesurce. Luego, utilizando activador.createInstance, podría lanzar a la interfaz y estaría configurado.

Algo como esto (nota - Cabeza compilada)

string[] MyClassNameList = { "Class1", "Class2" };

[TestCaseSource(MyClassNameList)]
public void Test1(string className)
{
    var instance = Activator.CreateInstance(Type.FromName(className)) as IMyInterface;

    ...
}

Puede usar los atributos [RowTest] en Mbunit para hacer esto. El siguiente ejemplo muestra dónde pasa el método una cadena para indicar qué clase de implementación de interfaz desea instanciar, y luego crea esta clase a través de la reflexión:

[RowTest]
[Row("Class1")]
[Row("Class2")]
[Row("Class3")]
public void TestMethod(string type)
{
   IMyInterface foo = Activator.CreateInstance(Type.GetType(type)) as IMyInterface;

   //Do tests on foo:
}

En los atributos [fila], puede pasar cualquier número arbitrario de parámetros de entrada, como los valores de entrada para las pruebas o los valores esperados que se devuelven mediante invocaciones de métodos. Deberá agregar argumentos de los tipos correspondientes como argumentos de entrada del método de prueba.

Esta es mi implementación concreta basada en Respuesta de Avandeursen:

[TestClass]
public abstract class IMyInterfaceTests
{
    protected abstract IMyInterface CreateInstance();

    [TestMethod]
    public void SomeTest()
    {
        IMyInterface instance = CreateInstance();
        // Run the test
    }
}

Cada implementación de la interfaz define la siguiente clase de prueba:

[TestClass]
public class MyImplementationTests : IMyInterfaceTests
{
    protected override IMyInterface CreateInstance()
    {
        return new MyImplementation();
    }
}

SomeTest se ejecuta una vez para cada concreto TestClass derivado de IMyInterfaceTests. Al usar una clase base abstracta, evito la necesidad de cualquier implementación simulada. Asegúrese de agregar TestClassAttribute a ambas clases o esto no funcionará. Por último, puede agregar cualquier prueba específica de implementación (como constructores) a la clase infantil si lo desea.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top