2010-09-16 23 views
8

Estoy tratando de comenzar a utilizar Pruebas unitarias en mi proyecto actual en Visual Studio 2010. Mi estructura de clase, sin embargo, contiene una serie de relaciones de herencia de clase abstracta e interfaz.Pruebas unitarias clases abstractas y/o interfaces

Si dos clases se derivan de la misma clase abstracta, o interfaz, me gustaría poder compartir el código de prueba entre ellas. No estoy seguro de cómo hacer esto exactamente. Creo que creo una clase de prueba para cada interfaz que quiero probar, pero no estoy seguro de la forma correcta de alimentar mis clases concretas en las pruebas unitarias aplicables.


actualización

bien aquí es un ejemplo. Digamos que tengo una interfaz IEmployee, que es implementada por una clase abstracta Employee, que luego es heredada por las dos clases concretas Worker y Employee. (El código se muestra a continuación)

Ahora digo que quiero crear pruebas que se apliquen a todos los empleados de IE o empleados. O, como alternativa, cree pruebas específicas para tipos específicos de empleados. Por ejemplo, puede querer afirmar que establecer IEmployer.Number en un número menor que cero para cualquier implementación de IEmployee arroja una excepción. Preferiría escribir las pruebas desde la perspectiva de cualquier empleado de IE y luego poder usar las pruebas en cualquier implementación de IEmployee.

Aquí hay otro ejemplo. También es posible que desee afirmar que establecer el tiempo de vacaciones para cualquier empleado a un valor inferior a cero tiradas y error. Sin embargo, es posible que también desee tener diferentes pruebas que se apliquen a una versión concreta específica de Employee. Digamos que quiero probar ese trabajador se produce una excepción si se proporcionan más de 14 días de vacaciones, pero un administrador puede proporcionar hasta 36.

public interface IEmployee 
{ 
    string Name {get; set;} 
    int Number {get; set;} 
} 


public abstract class Employee:IEmploee 
{ 
    string Name {get; set;} 
    int Number {get;set;} 
    public abstract int VacationTime(get; set;) 
} 


public abstract class Worker:IEmployee 
{ 
    private int v; 

    private int vTime; 
    public abstract int VacationTime 
    { 
     get 
     { 
     return VTime; 
     } 
     set 
     { 
     if(value>36) throw new ArgumentException("Exceeded allowed vaction"); 
     if(value<0)throw new ArgumentException("Vacation time must be >0"); 
     vTime= value; 
     } 
    } 


    public void DoSomWork() 
    { 
     //Work 
    } 
} 


public abstract class Manager:IEmployee 
{ 
    public abstract int VacationTime 
    { 
     get 
     { 
     return VTime; 
     } 
     set 
     { 
     if(value>14) throw new ArgumentException("Exceeded allowed vaction"); 
     if(value<0)throw new ArgumentException("Vacation time must be >0"); 
     vTime= value; 
     } 
    } 

    public void DoSomeManaging() 
    { 
     //manage 
    } 

} 

así que supongo que lo que estoy buscando es un flujo de trabajo eso me permitirá anidar pruebas unitarias. Entonces, por ejemplo, cuando pruebo la clase Manager, primero quiero probar que pasa las pruebas Employee e IEmployee, y luego probar miembros específicos como DoSomeManaging().

+0

No está claro qué estás tratando de hacer. Usted habla sobre interfaces de pruebas unitarias, pero esto no tiene sentido. ¿Qué estás tratando de probar exactamente?¿Podría proporcionar un ejemplo breve y conciso de su jerarquía de objetos y señalar qué es lo que quiere probar en esta jerarquía? –

+0

No quiero probar la interfaz por separado, pero quiero poder agrupar todas las pruebas que se aplican a una interfaz para poder aplicar fácilmente todas esas pruebas a cualquier clase que implemente la interfaz. Vea el ejemplo que agregué a mi pregunta. –

Respuesta

0

Lo que creo que quieres hacer es crear pruebas unitarias para los métodos en las clases abstractas.

No estoy seguro de que tenga sentido querer probar un método protegido en una clase abstracta, pero si insiste simplemente amplíe la clase en una clase utilizada exclusivamente para la prueba unitaria. De esta forma, puede exponer los métodos protegidos en la clase abstracta que desea probar a través de métodos públicos en la clase extendida que simplemente llama al método en la clase abstracta.

Si tiene métodos en clases abstractas que desea probar como unidad, sugiero refactorizarlos en clases separadas y simplemente exponerlos como métodos públicos y ponerlos bajo prueba. Intente ver su árbol de herencia desde la perspectiva de "probar primero" y estoy seguro de que también obtendrá esa solución (o una similar).

0

Parece que ha descrito "pruebas de unidades compuestas" que no son compatibles con las pruebas unitarias de Visual Studio 2010. Tales cosas se pueden hacer en MbUnit de acuerdo con este article. Es posible crear abstract tests en Visual Studio 2010, que probablemente no sea exactamente lo que desea. Here es una descripción de cómo implementar pruebas abstractas en VS (sección Ejemplo de herencia).

6

Supongo que sé a qué te refieres. Tuve el mismo problema.

Mi solución fue crear una jerarquía también para las pruebas.Usaré el mismo ejemplo que muestras.

Primero, tenga una clase de prueba abstracta para la base IEmployee.

Tiene dos cosas principales: i. Todos los métodos de prueba que quieras. ii. Un método abstracto que devuelve la instancia deseada de IEmployee.

[TestClass()] 
public abstract class IEmployeeTests 
{ 
    protected abstract GetIEmployeeInstance(); 

    [TestMethod()] 
    public void TestMethod1() 
    { 
    IEmployee target = GetIEmployeeInstance(); 
    // do your IEmployee test here 
    } 

} 

segundo lugar, usted tiene una clase de prueba para cada aplicación de IEmployee, la implementación del método abstracto y proporcionar instancias apropiadas de IEmployee.

[TestClass()] 
public class WorkerTests : IEmployeeTests 
{ 

    protected override GetIEmployeeInstance() 
    { 
     return new Worker(); 
    } 

} 

[TestClass()] 
public class ManagerTests : IEmployeeTests 
{ 

    protected override GetIEmployeeInstance() 
    { 
     return new Manager(); 
    } 

} 

Se puede ver que todo funciona como se esperaba y VS le da los métodos de ensayo previstos para cada WorkerTests y clases ManagerTests en la ventana TestView. Puede ejecutarlos y tener los resultados de las pruebas para cada implementación de la interfaz IEmployee, teniendo que crear las pruebas solo en la clase base IEmployeeTests.

Siempre puede agregar una prueba específica para las clases derivadas WorkerTests y ManagerTests.

La pregunta sería ahora, ¿qué pasa con las clases que implementan múltiples interfaces, digamos EmployedProgrammer?

public EmployedProgrammer : IEmployee, IProgrammer 
{ 
} 

No tenemos herencia múltiple en C#, así que esto no es una opción:

[TestClass()] 
public EmployedProgrammerIEmployeeTests : IEmployeeTests, IProgrammerTests 
{ 
    // this doesn't compile as IEmployeeTests, IProgrammerTests are classes, not interfaces 
} 

Para este escenario, una solución es tener las siguientes clases de prueba:

[TestClass()] 
public EmployedProgrammerIEmployeeTests : IEmployeeTests 
{ 
    protected override GetIEmployeeInstance() 
    { 
     return new EmployedProgrammer(); 
    } 
} 

[TestClass()] 
public EmployedProgrammerIProgrammerTests : IProgrammerTests 
{ 
    protected override GetIProgrammerInstance() 
    { 
     return new EmployedProgrammer(); 
    } 
} 

con

[TestClass()] 
public abstract class IProgrammerTests 
{ 
    protected abstract GetIProgrammerInstance(); 

    [TestMethod()] 
    public void TestMethod1() 
    { 
     IProgrammer target = GetIProgrammerInstance(); 
     // do your IProgrammerTest test here 
    } 

} 

Estoy usando esto con buenos resultados. Espero que ayude.

Saludos, José

+1

Esto se llama "prueba de contrato". Cuando trabaje de manera TDD, debería haber extraído la clase de prueba abstracta alrededor del tiempo en que extrajo la clase Employee en su código durante el paso de refactorización. –

0

Use moles de Microsoft para una mejor prueba. para que pueda burlarse de la clase base abstracta/métodos estáticos, etc. fácilmente. Por favor, consulte el siguiente post para obtener más información

detouring-abstract-base-classes-using-moles

BenzCar benzCar = new BenzCar();  
new MCar(benzCar) 
    { 
      Drive=() => "Testing" 
    }.InstanceBehavior = MoleBehaviors.Fallthrough; 

var hello = child.Drive(); 

Assert.AreEqual("Benz Car driving. Testing", hello); 
0

El deseo de correr la misma prueba contra múltiples clases por lo general significa que usted tiene la oportunidad de extraer el comportamiento que desea probar en una sola clase (si es la clase base o una clase completamente nueva que compones en tus clases existentes).

Tenga en cuenta su ejemplo: en lugar de aplicar límites de vacaciones en Worker y Manager, añadir una nueva variable miembro a Employee, 'MaximumVacationDays', aplicar el límite en la incubadora clase de empleado, y compruebe el límite de allí:

abstract class Employee { 
    private int maximumVacationDays; 
    protected Employee(int maximumVacationDays) { 
     this.maximumVacationDays = maximumVacationDays 
    } 
    public int VacationDays { 
     set { 
      if (value > maximumVacationDays) 
       throw new ArgumentException("Exceeded maximum vacation"); 
      } 
    } 
} 

class Worker: Employee { 
    public Worker(): Employee(14) {} 
} 

class Manager: Employee { 
    public Manager(): Employee(36) {} 
} 

Ahora solo tiene un método para probar y menos código para mantener.

Cuestiones relacionadas