2008-12-02 11 views
55

Esta pregunta se refiere a las pruebas unitarias en Visual Studio usando MSTest (esto es importante, debido a execution order de MSTest). Tanto el método marcado [TestInitialize] como el constructor de la clase de prueba se ejecutarán antes de cada método de prueba.¿Utiliza TestInitialize o el constructor de la clase de prueba para preparar cada prueba? ¿y por qué?

Entonces, la pregunta es, ¿qué tienden a hacer en cada una de estas áreas? ¿Evita realizar ciertas actividades en cualquiera de los dos? ¿Cuál es tu razón: estilo, técnica, superstición?

Respuesta

44

El constructor es solo una estructura proporcionada por el idioma. Cada marco de prueba parece tener su propio ciclo de vida controlado "inicializar". Probablemente solo tendrá problemas al usar el constructor para mutar sus locals.

MSTest: Obtiene una instancia completamente nueva de la clase de prueba para cada TestMethod. Este podría ser el único caso en el que está permitido cambiar las ubicaciones locales en el constructor, el inicializador o el método de prueba y no afectar los otros métodos de prueba.

public class TestsForWhatever 
{ 
    public TestsForWhatever() 
    { 
     // You get one of these per test method, yay! 
    } 

    [TestInitialize] 
    public void Initialize() 
    { 
     // and one of these too! 
    } 

    [TestMethod] 
    public void AssertItDoesSomething() { } 

    [TestMethod] 
    public void AssertItDoesSomethingElse() { } 
} 

MSpec: Sólo tienes una Establish y Because para todas sus afirmaciones (It). Por lo tanto, no mutes a tus lugareños en tus afirmaciones. Y no dependas de las mutaciones de los locales en contextos base (si los usas).

[Subject(typeof(Whatever))] 
public class When_doing_whatever 
{ 
    Establish context =() => 
    { 
     // one of these for all your Its 
    }; 

    Because of =() => _subject.DoWhatever(); 

    It should_do_something; 
    It should_do_something_else; 
} 
+0

Tuve el mismo problema y me acabas de dar la explicación correcta, ¡gracias! – Raffaeu

+4

También puede señalar que '[ClassInitialize]' solo se ejecuta una vez por ejecución de prueba (antes del todo), por lo que puede usarse para cualquier rutina de configuración costosa. – kmote

+4

-1: No deberías odiar tu pregunta; tiene mucho sentido, en realidad. Véase, por ejemplo, [xUnit.net] (https://github.com/xunit/xunit): recomienda usar el constructor como * el * inicializador de caso de prueba. Ser "solo una estructura proporcionada por el lenguaje" no es una cuestión menor; cualquiera que escriba cualquier tipo de marco (incluidos los marcos de prueba) * no * debe intentar reinventar la rueda, y usar en su lugar estándares bien definidos (como, ya sabes, usar constructores para inicializar cosas, etc.). – rsenna

1

El objeto que prueba no necesita ser instancied en el método [TestInitialize]. Puede probar el constructor de su objeto en un método de prueba [Prueba].

El objeto en [TestInitialize] puede ser configurar su almacenamiento de persistencia o preparar el valor que el objeto probado utilizará en las pruebas.

+2

Sé esto. Puede instanciar el objeto en línea con la declaración si es posible. Mi pregunta es qué haces en cualquiera de esos lugares. ¿Y por qué? –

+1

Respondí esa pregunta en mi respuesta ... volví a leer. –

+6

No respondió esa pregunta en su respuesta. Lee la pregunta nuevamente – Slauma

4

Prefiero usar el método [TestInitialize] para realizar una instanciación del objeto que se está probando y sus parámetros. Solo realizo trabajo en el constructor si es necesario crear una instancia de una clase base de prueba (que generalmente es donde creo o actualizo repositorios, etc.). Esto me ayuda a mantener el código de prueba y el código de prueba separados de forma lógica y física.

14

La principal ventaja de utilizar ya sea TestInitialize() o ClassInitialize() en lugar de la instancia de clase de prueba o constructores estáticos es su naturaleza explícita. Comunica claramente que está realizando una configuración antes de sus pruebas. Hacer esto consistentemente debería mejorar el mantenimiento a largo plazo.

+2

No estoy de acuerdo. ¿Qué podría ser más claro que un constructor? Claro, debes saber que obtienes una nueva instancia de clase de prueba para cada prueba, pero eso es algo que de todos modos deberías saber. Y no es como 'TestInitialize' es un nombre tan obvio que no puede causar confusión: https://stackoverflow.com/questions/22999816/testinitialize-vs-classinitialize. –

15

Aquí hay algunas ventajas que he encontrado con TestInitialize.

  • Algunas variables del entorno (por ejemplo, TestContext) no son accesibles hasta que se crea una instancia de la clase de prueba.
  • Puede requerir implementación con la clase derivada marcando un método base TestInitialize abstract.
  • Puede anular fácilmente un método TestInitialize base y determinar si se llama a la impl base antes de la impl derivada, después o en absoluto. Por el contrario, si deriva una clase de prueba de una clase de prueba base, en el caso de un constructor sin parámetros, se llamará al ctor base tanto si lo intentó como si no.
  • Su definición explícita aclara las intenciones y complementa el método TestCleanup. Podría argumentar que puede crear un destructor para cada constructor, pero no se garantiza que MS Test maneje los destructores como era de esperar.
+1

Encontré que MSTest * does * ejecuta un método 'public void Dispose' después de cada ejecución de prueba. Debe ser público, y no importa que implemente 'IDisposable': no puede implementar explícitamente la interfaz. – Sebazzz

0

Depende de la situación. Si tiene una clase de prueba, y por algún motivo extraño si necesita crear una instancia de la misma en otra clase de prueba, necesitará usar el constructor.

De lo contrario, la prueba de inicialización encaja más en el concepto. En primer lugar, las mismas razones escritas anteriormente, el segundo MS puede introducir más características en ese atributo y usted las beneficiará, con el constructor se quedará atrapado en él.

1

Digo usar el constructor a menos que necesite TestContext.

  1. Si puede mantener las cosas simples, ¿por qué no? Un constructor es más simple que un atributo mágico.
  2. Puede usar readonly, que es una gran cosa en la inicialización de pruebas en la que desea preparar cosas para las pruebas que se supone que no deben cambiar (idealmente, las cosas que prepare también serían inmutables).
+1

+1 para el readonly. Acabo de eliminar mi pregunta al respecto cuando vi su respuesta. Pero normalmente pensaría de otra manera: uso TestInitialize a menos que quiera un atributo de solo lectura, ya que se ve un mejor ajuste de "idioma" en el contexto de prueba. Pero es solo una decisión de diseño. Estoy bien con ambos enfoques. – heringer

Cuestiones relacionadas