J'essaie d'utiliser Jasmine pour écrire certaines spécifications BDD pour les requêtes jQuery AJAX de base. J'utilise actuellement Jasmine en mode autonome (c'est-à-dire par le biais de SpecRunner.html
). J'ai configuré SpecRunner pour charger jquery et d'autres fichiers .js. Des idées pourquoi le suivant ne fonctionne pas? has_returned ne devient pas vrai, même pensé le "yuppi!" alerte montre bien.
describe("A jQuery ajax request should be able to fetch...", function() {
it("an XML file from the filesystem", function() {
$.ajax_get_xml_request = { has_returned : false };
// initiating the AJAX request
$.ajax({ type: "GET", url: "addressbook_files/addressbookxml.xml", dataType: "xml",
success: function(xml) { alert("yuppi!"); $.ajax_get_xml_request.has_returned = true; } });
// waiting for has_returned to become true (timeout: 3s)
waitsFor(function() { $.ajax_get_xml_request.has_returned; }, "the JQuery AJAX GET to return", 3000);
// TODO: other tests might check size of XML file, whether it is valid XML
expect($.ajax_get_xml_request.has_returned).toEqual(true);
});
});
Comment puis-je tester que le rappel a été appelé? Tout pointeur sur les blogs/matériel lié au test asynchrone de jQuery avec Jasmine sera grandement apprécié.
Je suppose qu'il y a deux types de tests que vous pouvez faire:
Jasmine peut vous aider à faire les deux types de tests.
Voici un exemple de la façon dont vous pouvez simuler la demande AJAX, puis écrire un test unitaire pour vérifier que la demande fictive AJAX était dirigée vers l'URL correcte:
it("should make an AJAX request to the correct URL", function() {
spyOn($, "ajax");
getProduct(123);
expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/products/123");
});
function getProduct(id) {
$.ajax({
type: "GET",
url: "/products/" + id,
contentType: "application/json; charset=utf-8",
dataType: "json"
});
}
Pour Jasmine 2.0, utilisez plutôt:
expect($.ajax.calls.mostRecent().args[0]["url"]).toEqual("/products/123");
comme indiqué dans cette réponse
Voici un test unitaire similaire qui vérifie que votre rappel a été exécuté, à l'issue d'une demande AJAX terminée avec succès:
it("should execute the callback function on success", function () {
spyOn($, "ajax").andCallFake(function(options) {
options.success();
});
var callback = jasmine.createSpy();
getProduct(123, callback);
expect(callback).toHaveBeenCalled();
});
function getProduct(id, callback) {
$.ajax({
type: "GET",
url: "/products/" + id,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: callback
});
}
Pour Jasmine 2.0, utilisez plutôt:
spyOn($, "ajax").and.callFake(function(options) {
comme indiqué dans cette réponse
Enfin, vous avez laissé entendre ailleurs que vous souhaiteriez peut-être écrire des tests d'intégration faisant de vraies requêtes AJAX, à des fins d'intégration. Cela peut être fait en utilisant les fonctionnalités asynchrones de Jasmine: waits (), waitsFor () et runs ():
it("should make a real AJAX request", function () {
var callback = jasmine.createSpy();
getProduct(123, callback);
waitsFor(function() {
return callback.callCount > 0;
});
runs(function() {
expect(callback).toHaveBeenCalled();
});
});
function getProduct(id, callback) {
$.ajax({
type: "GET",
url: "data.json",
contentType: "application/json; charset=utf-8"
dataType: "json",
success: callback
});
}
Regardez le projet jasmine-ajax: http://github.com/pivotal/jasmine-ajax .
Il s'agit d'un assistant immédiat (que ce soit pour jQuery ou Prototype.js) au niveau de la couche XHR afin que les demandes ne soient jamais envoyées. Vous pouvez alors attendre tout ce que vous voulez de la demande.
Ensuite, il vous permet de fournir des réponses fixes pour tous vos cas, puis d’écrire des tests pour chaque réponse souhaitée: succès, échec, non autorisé, etc.
Il permet aux appels Ajax de sortir du domaine des tests asynchrones et vous offre une grande flexibilité pour tester le fonctionnement de vos gestionnaires de réponses réels.
voici un exemple de suite de tests simple pour une application comme celle-ci
var app = {
fire: function(url, sfn, efn) {
$.ajax({
url:url,
success:sfn,
error:efn
});
}
};
un exemple de suite de tests, qui appellera un rappel basé sur l'URL regexp
describe("ajax calls returns", function() {
var successFn, errorFn;
beforeEach(function () {
successFn = jasmine.createSpy("successFn");
errorFn = jasmine.createSpy("errorFn");
jQuery.ajax = spyOn(jQuery, "ajax").andCallFake(
function (options) {
if(/.*success.*/.test(options.url)) {
options.success();
} else {
options.error();
}
}
);
});
it("success", function () {
app.fire("success/url", successFn, errorFn);
expect(successFn).toHaveBeenCalled();
});
it("error response", function () {
app.fire("error/url", successFn, errorFn);
expect(errorFn).toHaveBeenCalled();
});
});
Lorsque je spécifie du code ajax avec Jasmine, je résous le problème en espionnant la fonction dépendante qui initie l'appel distant (comme, par exemple, $ .get ou $ ajax). Ensuite, je récupère les callbacks définis dessus et les teste discrètement.
Voici un exemple que j'ai récemment expliqué:
Essayez jqueryspy.comIl fournit une syntaxe élégante, semblable à celle de jquery, pour décrire vos tests et permet aux callbacks de tester une fois l’ajustement terminé. Il est idéal pour les tests d’intégration et vous pouvez configurer des temps d’attente ajax maximum en secondes ou en millisecondes.
J'ai le sentiment que je dois fournir une réponse plus à jour puisque Jasmine est maintenant à la version 2.4 et que quelques fonctions ont été modifiées par rapport à la version 2.0.
Ainsi, pour vérifier qu'une fonction de rappel a été appelée dans votre demande AJAX, vous devez créer un espion, y ajouter une fonction callFake, puis utiliser l'espion comme fonction de rappel. Voici comment ça se passe:
describe("when you make a jQuery AJAX request", function()
{
it("should get the content of an XML file", function(done)
{
var success = jasmine.createSpy('success');
var error = jasmine.createSpy('error');
success.and.callFake(function(xml_content)
{
expect(success).toHaveBeenCalled();
// you can even do more tests with xml_content which is
// the data returned by the success function of your AJAX call
done(); // we're done, Jasmine can run the specs now
});
error.and.callFake(function()
{
// this will fail since success has not been called
expect(success).toHaveBeenCalled();
// If you are happy about the fact that error has been called,
// don't make it fail by using expect(error).toHaveBeenCalled();
done(); // we're done
});
jQuery.ajax({
type : "GET",
url : "addressbook_files/addressbookxml.xml",
dataType : "xml",
success : success,
error : error
});
});
});
J'ai fait le tour pour la fonction de succès ainsi que la fonction d'erreur pour m'assurer que Jasmine exécutera les spécifications le plus rapidement possible, même si votre AJAX renvoie une erreur.
Si vous ne spécifiez pas de fonction d'erreur et que votre AJAX renvoie une erreur, vous devrez attendre 5 secondes (intervalle de délai d'attente par défaut) jusqu'à ce que Jasmine génère une erreur Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
. Vous pouvez également spécifier votre propre délai d'attente comme ceci:
it("should get the content of an XML file", function(done)
{
// your code
},
10000); // 10 seconds