Pregunta

Estoy usando C # 3.0 y NUnit. Me pregunto si hay una forma estándar de realizar pruebas unitarias en el código que se ejecuta después de cierto tiempo. Por ejemplo, tengo una clase estática simple con la que puedo registrar métodos y hacer que se llamen n milisegundos más tarde. Necesito asegurarme de que se llama al código en los métodos delegados.

Por ejemplo, la siguiente prueba siempre pasará, porque no se hacen afirmaciones hasta después de que el método haya salido.

[Test]
public void SampleTest()
{
    IntervalManager.SetTimeout(delegate{ 
        Assert.Equals(now.Millisecond + 100, DateTime.Now.Millisecond); 
    }, 100);
}

¿Es posible incluso unificar el código de prueba que no se ejecuta inmediatamente?

Saludos,

Paul

¿Fue útil?

Solución

¿Qué tal esto? Hace que la prueba se bloquee durante un tiempo máximo esperado para que se active la devolución de llamada y se complete antes del rescate, informando un error.

public void Foo() {
    AutoResetEvent evt = new AutoResetEvent(false);
    Timer t = new Timer(state => {
        // Do work
        evt.Set();
    }, null, 100, Timeout.Infinite);
    if (evt.WaitOne(500)) {
        // method called and completed
    } else {
        // timed out waiting
    }
}

Otros consejos

Entonces, ¿qué estás probando exactamente? ¿Estás probando que los temporizadores funcionan? ¿O que su código configura correctamente un temporizador para que al vencimiento el temporizador ejecute una devolución de llamada? Sin saber cómo se ve el código, supongo que lo que realmente desea probar es lo último. Mi respuesta sería que (1) esto probablemente será difícil con los métodos estáticos y (2) probablemente necesitará usar la inyección de dependencia e inyectar los temporizadores simulados, etc. que en realidad no ejecutan el método resultante, sino que registran a través de las expectativas de que su código hizo las llamadas adecuadas.

Como nota al margen. Por lo general, intentamos marcar nuestras pruebas de ejecución lenta con una categoría NUnit y puede optar por omitir esas pruebas en algunas compilaciones.

Sí, hacerlo como en su ejemplo no funcionará.

En cambio, le recomendaría que cree una clase de prueba, que se utilizará como delegado, que registre cuándo se llamó a su método y a qué hora.

Luego inyecta su simulacro en el IntervalManager que desea probar. Su método de prueba debe esperar al IntervalManager (utilizando un método adecuado proporcionado por el IntervalManager, o simplemente esperar unos segundos), luego puede verificar el estado de la clase de prueba.

Por cierto, este enfoque generalmente se conoce como burlarse ; en este caso, la clase de prueba sería el objeto simulado.

Como un par de otras respuestas han indicado, las pruebas de larga duración son generalmente una mala idea. Para facilitar la prueba de este componente, debe considerar que realmente tiene dos cosas distintas que está tratando de probar.

  1. Cuando registra una ejecución de delegado programada, se establece el tiempo adecuado. Es probable que esto deba probarse con varias combinaciones de tiempos de espera y números de delegados.
  2. El delegado se ejecuta de la manera adecuada.

Separar las pruebas de la manera le permitiría comprobar que su mecanismo de temporización funciona como se esperaba con un pequeño número de tiempos de espera cortos (probando todos los casos que debe considerar). Recuerde que probablemente necesitará un poco de margen de maniobra en el tiempo real que lleva ejecutar un delegado determinado en función de la carga actual en el sistema y la complejidad del código de su componente (es decir, en IntervalManager ).

Por supuesto que puedes probarlo. Solo tiene que esperar a que se ejecute.

Tal vez me estoy perdiendo algo, pero las pruebas unitarias de Visual Studio tienen atributos especiales que puede poner en métodos para controlar el orden de ejecución y otras cosas. Esto debería haberse generado automáticamente cuando realizó por primera vez el proyecto de prueba de unidad:

    #region Additional test attributes
    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    //[ClassInitialize]
    //public static void MyClassInitialize(TestContext testContext) {
    //}
    //
    //Use ClassCleanup to run code after all tests in a class have run
    //[ClassCleanup()]
    //public static void MyClassCleanup()
    //{
    //}
    //
    //Use TestInitialize to run code before running each test
    //[TestInitialize()]
    //public void MyTestInitialize()
    //{
    //}
    //
    //Use TestCleanup to run code after each test has run
    //[TestCleanup()]
    //public void MyTestCleanup()
    //{
    //}
    //
    #endregion

Por lo tanto, usar [ClassInitialize] debería permitirle escribir lo que sea necesario ejecutar primero en un método. Entonces tus pruebas pueden ejecutarse. O puede usar [TestInitialize] para ejecutar código antes de cada prueba.

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