如何在不使用超时的情况下测试 angularjs 控制器异步行为
How to test angularjs controller async behavior without using Timeout
所以我有一个这样的控制器:
angular.module('someModule').controller('someController',function(productService) {
$scope.products = [];
$scope.init = function() {
aService.fetchAll().then(function(payload) {
$scope.products = filterProducts(payload.data);
});
}
$scope.init();
function filterProducts(products) {
//for each of products filter some specific ones
return filteredProducts;
}
});
我正在编写一个将调用 $scope.init() 的测试,并且必须验证产品是否已正确过滤。我在模拟 $httpBackend 所以代码看起来像这样:
describe("someController", function() {
"use strict";
var $controller; //factory
var controller; //controller
var $rootScope;
var $state;
var $stateParams;
var $injector;
var $scope;
var $httpBackend;
var productService;
beforeEach(function(){
angular.mock.module("someModule")
inject(function (_$rootScope_, _$state_, _$injector_, $templateCache, _$controller_, _$stateParams_, _$httpBackend_, _productService_) {
$rootScope = _$rootScope_;
$state = _$state_;
$stateParams = _$stateParams_;
$injector = _$injector_;
$controller = _$controller_;
$httpBackend = _$httpBackend_;
productService = _productService_;
});
controller = $controller("someController", {$scope: $scope, $state: $state});
});
it("init() should filter products correctly",function(){
//Arrange
var expectedFilteredProducts = ["1","2"];
var products = ["0","1","2"];
$httpBackend.whenGET("api/products").respond(products);
//Act
$scope.init();
//Assert
setTimeout(100,function(){
expect($scope.products).toEqual(expectedFilteredProducts);
});
$httpBackend.flush();
});
});
问题是没有 setTimeout 测试不会通过。有没有一种方法可以测试我在没有它的情况下尝试做的事情,而不是为了测试而引入复杂的 $q/promises ?作为旁注,productService 正在返回一个承诺 $http。谢谢
编辑:setTimeout 进行测试 运行 但没有断言发生..
根据评论,问题是 $httpBackend.flush()
需要在预期之前执行。它有效地解决了所有被训练解决的请求(以及它们各自的承诺),以便期望可以 运行 完成承诺。
测试后使用 $httpBackend.verifyNoOutstandingExpectation()
/ $httpBackend.verifyNoOutstandingRequest()
也是个好主意。
另请注意,控制器已经调用了 scope.init(),因此在测试中再次调用它是多余的 - 根据具体情况甚至可能导致失败。
同样适用于 setTimeout
运行ning 但不声明任何内容:在规范中使用它会使该规范异步。您必须使用 done
回调定义规范,并在测试真正完成时从异步代码中调用它,如:
it("init() should filter products correctly",function(done) {
...
//Assert
setTimeout(100, function() {
...
done();
});
});
再次注意,Angular 模拟提供了在 99.9% 的时间里避免使用 setTimeout
的方法,甚至 $interval
/$timeout
服务也被正确地模拟了!
所以我有一个这样的控制器:
angular.module('someModule').controller('someController',function(productService) {
$scope.products = [];
$scope.init = function() {
aService.fetchAll().then(function(payload) {
$scope.products = filterProducts(payload.data);
});
}
$scope.init();
function filterProducts(products) {
//for each of products filter some specific ones
return filteredProducts;
}
});
我正在编写一个将调用 $scope.init() 的测试,并且必须验证产品是否已正确过滤。我在模拟 $httpBackend 所以代码看起来像这样:
describe("someController", function() {
"use strict";
var $controller; //factory
var controller; //controller
var $rootScope;
var $state;
var $stateParams;
var $injector;
var $scope;
var $httpBackend;
var productService;
beforeEach(function(){
angular.mock.module("someModule")
inject(function (_$rootScope_, _$state_, _$injector_, $templateCache, _$controller_, _$stateParams_, _$httpBackend_, _productService_) {
$rootScope = _$rootScope_;
$state = _$state_;
$stateParams = _$stateParams_;
$injector = _$injector_;
$controller = _$controller_;
$httpBackend = _$httpBackend_;
productService = _productService_;
});
controller = $controller("someController", {$scope: $scope, $state: $state});
});
it("init() should filter products correctly",function(){
//Arrange
var expectedFilteredProducts = ["1","2"];
var products = ["0","1","2"];
$httpBackend.whenGET("api/products").respond(products);
//Act
$scope.init();
//Assert
setTimeout(100,function(){
expect($scope.products).toEqual(expectedFilteredProducts);
});
$httpBackend.flush();
});
});
问题是没有 setTimeout 测试不会通过。有没有一种方法可以测试我在没有它的情况下尝试做的事情,而不是为了测试而引入复杂的 $q/promises ?作为旁注,productService 正在返回一个承诺 $http。谢谢
编辑:setTimeout 进行测试 运行 但没有断言发生..
根据评论,问题是 $httpBackend.flush()
需要在预期之前执行。它有效地解决了所有被训练解决的请求(以及它们各自的承诺),以便期望可以 运行 完成承诺。
测试后使用 $httpBackend.verifyNoOutstandingExpectation()
/ $httpBackend.verifyNoOutstandingRequest()
也是个好主意。
另请注意,控制器已经调用了 scope.init(),因此在测试中再次调用它是多余的 - 根据具体情况甚至可能导致失败。
同样适用于 setTimeout
运行ning 但不声明任何内容:在规范中使用它会使该规范异步。您必须使用 done
回调定义规范,并在测试真正完成时从异步代码中调用它,如:
it("init() should filter products correctly",function(done) {
...
//Assert
setTimeout(100, function() {
...
done();
});
});
再次注意,Angular 模拟提供了在 99.9% 的时间里避免使用 setTimeout
的方法,甚至 $interval
/$timeout
服务也被正确地模拟了!