简体   繁体   English

您如何延迟测试角度承诺?

[英]How do you test angular promises with delays?

How do I run a test with a mocked angular and a promise that is resolved after some time? 如何在模拟的角度和经过一定时间的承诺后运行测试?

Put more simply: the test below will never run 简单地说:下面的测试将永远不会运行

 var injector = angular.injector(['ngMock']); var scope = injector.get('$rootScope').$new(); var q = injector.get('$q'); var promise = function() { return q(function(resolve, reject) { setTimeout(function() { resolve(); }, 500); }); }; promise() .then(function() { document.getElementById('result').innerHTML = 'TEST RUN'; }); //resolve the promises scope.$digest(); 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular-mocks.js"></script> <p id="result">starting test...</p> 

Ok I've got it. 好的,我知道了。

The solution is actually the one proposed by @mido22, but I prefer a slightly different version, which you can derive from the documentation of $scope and inprog. 该解决方案实际上是@ mido22提出的解决方案,但我更喜欢一个略有不同的版本,您可以从$ scope和inprog的文档中获得该版本。

According to the angular documentation , you need to call $apply() when you are working in asynchronous functions like, it says explicitly, setTimeout. 根据有角度的文档 ,在异步函数(如setTimeout)中,您需要调用$ apply()。 Here the explanation is better: 这里的解释更好:

code that is being trigger directly as a call back to some external event, from the DOM or 3rd party library, should expect that it is never called from within Angular, and so any Angular application code that it calls should first be wrapped in a call to $apply." 从DOM或第3方库调用作为外部事件的回调而直接触发的代码应该期望从不从Angular内部调用,因此,它调用的所有Angular应用程序代码都应首先包装在调用中申请。”

So the solution is to wrap all calls that would affect Angular (including resolve() and reject()) into an $apply. 因此解决方案是将所有会影响Angular的调用(包括resolve()和reject())包装到$ apply中。

To avoid inprog errors, one must not call $apply() inside another. 为避免出现inprog错误,一个用户不得在另一个内部调用$ apply()。 It's not the case in this example, but, supposing we had another nested setTimeout, only the last to be called should call $apply(). 在此示例中不是这种情况,但是,假设我们还有另一个嵌套的setTimeout,则只有最后一个要调用的函数才应调用$ apply()。

UPDATE : 更新

according to this , the best way to avoid inprog errors is to wrap the non-angular code inside a $timeout(). 根据 ,以避免错误inprog最好的方式是包装一个$超时内的无角的代码()。 It's the strategy recommended by angular guys. 这是有角人推荐的策略。

 var injector = angular.injector(['ngMock']); var scope = injector.get('$rootScope').$new(); var q = injector.get('$q'); var promise = function() { return q(function(resolve, reject) { setTimeout(function() { scope.$apply(function() { resolve(); }); }, 500); }); }; promise() .then(function() { document.getElementById('result').innerHTML = 'TEST RUN'; }); 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular-mocks.js"></script> <p id="result">starting test...</p> 

apparantly, $q is integrated with the $rootScope.Scope Scope model observation mechanism in angular, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI. 显然, $q与$ rootScope.Scope Scope模型观察机制集成在角度中,这意味着可以更快地将分辨率或拒绝项传播到模型中,并避免不必要的浏览器重绘,这将导致UI闪烁。 ( for more details, check differences between Q and $q in docs ). (有关更多详细信息,请检查docs中的 Q和$ q之间的差异)。 I have just added scope.$apply() to your snippet: 我刚刚在您的代码段中添加了scope.$apply()

 var injector = angular.injector(['ngMock']); var scope = injector.get('$rootScope').$new(); var q = injector.get('$q'); var promise = function() { return q(function(resolve, reject) { setTimeout(function() { resolve(); scope.$apply(); }, 500); }); }; promise() .then(function() { document.getElementById('result').innerHTML = 'TEST RUN'; }); //resolve the promises scope.$digest(); 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular-mocks.js"></script> <p id="result">starting test...</p> 

So you shouldn't use the setTimeout function with angular instead you can use the $timeout service provided by angular. 因此,您不应将setTimeout函数与angular一起使用,而可以使用angular提供的$timeout服务。 Within a unit test things like timeouts are not going to be able to effectively calculated and controlled like they would in a page. 在单元测试中,超时等内容将无法像在页面中那样有效地计算和控制。 In order to resolve the timeouts you would use $timeout.flush() and instead do it like this: 为了解决超时问题,您可以使用$timeout.flush()来代替:

var injector = angular.injector(['ngMock']);
var scope = injector.get('$rootScope').$new();
var q = injector.get('$q');
var timeout = injector.get('$timeout');

var promise = function() {
  return q(function(resolve, reject) {
    timeout(function() {
      resolve();
      scope.$apply();
    }, 500);
  });
};

promise()
  .then(function() {
    document.getElementById('result').innerHTML = 'TEST RUN';
  });

//resolve the promises
timeout.flush();
scope.$digest();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM