Se mettre “$digest déjà en cours” dans async test avec le Jasmin 2.0
Je sais que l'appel $digest
ou $apply
manuellement pendant un condensé cycle de provoquer un "$digest déjà en cours" erreur, mais je n'ai aucune idée de pourquoi je suis ici.
C'est un test unitaire pour un service qui intègre $http
, le service est assez simple, il empêche de prise de dupliquer des appels vers le serveur tout en s'assurant que le code qui tente de faire les appels obtient toujours les données attendues.
angular.module('services')
.factory('httpService', ['$http', function($http) {
var pendingCalls = {};
var createKey = function(url, data, method) {
return method + url + JSON.stringify(data);
};
var send = function(url, data, method) {
var key = createKey(url, data, method);
if (pendingCalls[key]) {
return pendingCalls[key];
}
var promise = $http({
method: method,
url: url,
data: data
});
pendingCalls[key] = promise;
promise.then(function() {
delete pendingCalls[key];
});
return promise;
};
return {
post: function(url, data) {
return send(url, data, 'POST');
},
get: function(url, data) {
return send(url, data, 'GET');
},
_delete: function(url, data) {
return send(url, data, 'DELETE');
}
};
}]);
L'unité de test est également assez simple, il utilise $httpBackend
d'attendre la demande.
it('does GET requests', function(done) {
$httpBackend.expectGET('/some/random/url').respond('The response');
service.get('/some/random/url').then(function(result) {
expect(result.data).toEqual('The response');
done();
});
$httpBackend.flush();
});
Ce souffle que sone comme done()
est appelée avec un "$digest déjà en cours d'erreur". Je n'ai aucune idée pourquoi. Je peux résoudre ce problème en emballage done()
dans un délai d'expiration comme ce
setTimeout(function() { done() }, 1);
Qui signifie done()
obtiendrez en file d'attente et de courir après le $digest est fait mais tout cela résout mon problème, je veux savoir
- Pourquoi est-Angulaire dans un condensé du cycle en premier lieu?
- Pourquoi appeler
done()
déclencher cette erreur?
J'ai eu exactement le même test de course vert au Jasmin 1.3, cela s'est produit seulement après que j'ai mis à Jasmine 2.0 et réécrit le test à utiliser le nouveau asynchrone de syntaxe.
- qu'est-ce que
done()
? - C'est la nouvelle façon de traiter avec async tests de Jasmin 2.0. Il est injecté dans le test-fonction et si vous n'avez pas appelé dans les 5 secondes, le test échoue. Voir le jasmin.github.io/2.0/...
- Il n'y a pas de balise pour jasmine 2.0, ou je l'aurais marqué. Je peux voir comment la syntaxe est source de confusion si vous ne l'avez pas vu avant.
- OK, j'ai passé presque toute la journée hier, mais je pense que j'ai tout compris. Je vais l'ajouter comme une réponse, et j'ai peut-être tort.
- que ça ressemble à une belle recherche, je vais voir si je confirme ce que vous avez trouvé.
- s'il vous plaît ne, et après ce que vous trouvez.
- Je vois la même chose que vous voyez. Bon travail de creuser dans ce.
- Heureux que j'ai trouvé cela, mais triste que tout le monde est probablement de passer un jour sur ce
Vous devez vous connecter pour publier un commentaire.
$httpBacked.flush()
commence et termine une$digest()
cycle. J'ai passé toute la journée d'hier à creuser dans le code source de ngResource et anguleuses se moque d'obtenir au fond de ce, et encore ne comprennent pas entièrement.Autant que je peux dire, le but de
$httpBackend.flush()
est d'éviter la async structure au-dessus de tout. En d'autres mots, la syntaxe deit('should do something',function(done){});
et$httpBackend.flush()
ne jouent pas bien ensemble. La très but de.flush()
est de pousser à travers l'attente de l'async rappels et de revenir ensuite. C'est comme un grosdone
wrapper autour de l'ensemble de votre async rappels.Donc si j'ai bien compris (et ça marche pour moi maintenant) la bonne méthode serait de retirer le
done()
processeur lors de l'utilisation de$httpBackend.flush()
:Si vous ajoutez de la console.journal des états, vous trouverez que tous les rappels constamment se produire au cours de la
flush()
cycle:Alors la sortie sera:
À chaque fois. Si vous voulez vraiment la voir, de saisir la portée et de regarder
scope.$$phase
Et vous verrez le résultat:
$httpBackend.flush()
va encore attendre pour tout terminer avant le retour. À moins que je ne comprenais pas comment vous voulez introduire un retard?@deitch est droit, que
$httpBacked.flush()
déclenche une digérer. Le problème est que lorsque$httpBackend.verifyNoOutstandingExpectation();
est exécuté après chaqueit
est terminé, il dispose également d'un résumé. Voici donc la séquence des événements:flush()
qui déclenche un condenséthen()
est exécutéedone()
est exécutéeverifyNoOutstandingExpectation()
est exécuté qui déclenche un résumé, mais vous êtes déjà dans une, si vous obtenez une erreur.done()
est toujours important, car nous devons savoir que "s'attend à", dans lethen()
sont même exécuté. Si lethen
ne fonctionne pas, alors vous pourriez sais, maintenant, il y a eu des échecs. La clé est de s'assurer que le dossier est complet avant le déclenchement de ladone()
.Mettre
done()
dans un délai d'attente fera elle s'exécute immédiatement après le résumé actuel est terminée(). Cela permettra d'assurer que tous lesexpects
que vous voulez exécuter fonctionneront.setTimeout
solution était ce que j'essayais d'éviter en premier lieu 🙂L'ajout de @deitch de réponse. Pour faire les tests plus robuste, vous pouvez ajouter un espion avant de votre rappel. Cela devrait garantir que votre callback est appelée.