2012-02-24 10 views
6

Estamos tratando de implementar pruebas QUnit JavaScript para una aplicación web pesada JS. Estamos luchando para encontrar una forma de probar con éxito los métodos que involucran solicitudes jQuery AJAX. Por ejemplo, tenemos la siguiente función constructora (obviamente este es un ejemplo muy simplista):Pruebas unitarias Solicitudes AJAX con QUnit

var X = function() { 
    this.fire = function() { 
     $.ajax("someURL.php", { 
      data: { 
       userId: "james" 
      }, 
      dataType: "json", 
      success: function(data) { 
       //Do stuff 
      } 
     }); 
    }; 
}; 
var myX = new X(); 
myX.fire(); 

Estamos tratando de encontrar una manera de probar el método fire, preferiblemente con una URL aplastó en lugar de la verdadera someURL.php.

La única solución obvia para mí en este momento es agregar la URL y la devolución de llamada success, como argumentos para la función de constructor. De esa manera, en la prueba, podemos crear una nueva instancia de X y pasar la URL del resguardo y una devolución de llamada para ejecutar cuando el resguardo devuelva una respuesta. Por ejemplo:

test("Test AJAX function", function() { 
    stop(); 
    var myX = new X(); 
    //Call the AJAX function, passing in the stub URL and success callback 
    myX.fire("stub.php", function(data) { 
     console.log(data); 
     start(); 
    }); 
}); 

Sin embargo, esto no parece una solución muy buena. ¿Hay una mejor manera?

Respuesta

9

con jQuery, puede utilizar el objeto XHR que .ajax() regresa como una promesa, por lo que puede añadir más controladores (véase más adelante) que sólo los individuales success, complete y error los que se definen en las opciones. Entonces, si su función asíncrona puede devolver el objeto xhr, puede agregar manejadores específicos de la prueba.

En cuanto a la URL, eso es un poco más complicado. A veces configuré un servidor de Nodo muy simple en localhost, que solo sirve respuestas enlatadas que fueron copiadas del servidor real. Si ejecuta su suite de pruebas fuera del mismo servidor, sus URL solo deben ser rutas absolutas para acceder al servidor de prueba en lugar de al servidor de producción. Y también obtiene un registro de las solicitudes, como un servidor las ve. O puede hacer que el servidor de prueba envíe errores o respuestas erróneas a propósito, si desea ver cómo lo maneja el código.

Pero eso es, por supuesto, una solución bastante compleja. El más fácil sería definir sus URL en un lugar donde pueda redefinirlas desde el banco de pruebas. Por ejemplo:

/* in your code */ 
var X = function() { 
    this.fire = function() { 
     return $.ajax({ url: this.constructor.url, ... }); 
    }; 
}; 
X.url = "someURL.php"; // the production url 

/* in your tests */ 
X.url = "stub.php"; // redefine to the test url 

Además, QUnit tiene una función asyncTest, que llama stop() para usted. Agregue un pequeño ayudante para realizar un seguimiento de cuándo volver a comenzar, y tiene una solución bastante buena.

Esto es lo que he hecho antes

// create a function that counts down to `start()` 
function createAsyncCounter(count) { 
    count = count || 1; // count defaults to 1 
    return function() { --count || start(); }; 
} 

// .... 

// an async test that expects 2 assertions 
asyncTest("testing something asynchronous", 2, function() { 
    var countDown = createAsyncCounter(1), // the number of async calls in this test 
     x = new X; 

    // A `done` callback is the same as adding a `success` handler 
    // in the ajax options. It's called after the "real" success handler. 
    // I'm assuming here, that `fire()` returns the xhr object 
    x.fire().done(function(data, status, jqXHR) { 
     ok(data.ok); 
     equal(data.value, "foobar"); 
    }).always(countDown); // call `countDown` regardless of success/error 
}); 

Básicamente countDown es una función que cuenta regresiva a cero de lo que especifique, y luego llama start(). En este caso, hay 1 llamada asincrónica, por lo que countDown realizará una cuenta atrás desde allí. Y lo hará cuando finalice la llamada ajax, independientemente de cómo fue, ya que está configurada como una devolución de llamada always.
Y como se dice que el asyncTest espera 2 aserciones, informará de un error si nunca se llama a la devolución de llamada .done(), ya que no se ejecutarán las aserciones. Entonces, si la llamada falla completamente, lo sabrá también. Si desea registrar algo por error, puede agregar una devolución de llamada .fail() a la cadena de promesas.

+0

Esto es genial. Muchas gracias :) –

2

Intente utilizar jQuery spy. He creado una forma más fácil de usar la sintaxis familiar de jQuery para probar ajax.

Check this link

3

Si se trata de una prueba de unidad que puede (y debe) ser ejecutado de forma aislada del lado del servidor, puede simplemente "reemplazar" $.ajax para simular cualquier comportamiento. Un ejemplo fácil:

test("Test AJAX function", function() { 
    // keep the real $.ajax 
    var _real_ajax = $.ajax; 

    // Simulate a successful response 
    $.ajax = function(url, opts) { 
    opts.success({expected: 'response'}); 
    } 

    var myX = new X(); 
    // Call your ajax function 
    myX.fire(); 
    // ... and perform your tests 

    // Don't forgot to restore $.ajax! 
    $.ajax = _real_ajax; 
}); 

Obviamente también se puede llevar a cabo una verdadera llamada AJAX con aplastó url/datos:

// Simulate a successfully response 
$.ajax = function(url, opts) { 
    opts.success = function(data) { 
    console.log(data); 
    start(); 
    } 
    _real_ajax('stub.php', opts) 
} 

Si usted no tiene una respuesta compleja, prefiero la primera aproximación, porque es más rápido y fácil de comprender
Sin embargo, también puede tomar otro camino y poner la lógica de Ajax en su propio método, para que pueda insertarla fácilmente durante las pruebas.

+0

Para aislar mis pruebas del lado del servidor, tuve mucho éxito con esta biblioteca https://github.com/jakerella/jquery-mockjax –