Javascript 嵌套的AngularJS承诺不会在单元测试中工作
我正在学习如何使用AngularJS承诺,我在为它们编写单元测试时遇到了问题。我编写了一个带有Javascript 嵌套的AngularJS承诺不会在单元测试中工作,javascript,angularjs,unit-testing,jasmine,Javascript,Angularjs,Unit Testing,Jasmine,我正在学习如何使用AngularJS承诺,我在为它们编写单元测试时遇到了问题。我编写了一个带有工厂的模块,它可以查询RSS提要,解析文章中的标题,并返回一个承诺,该承诺将解析标题字符串数组 以下是模块代码: angular.module('rss', []) .factory('rssService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) { return function(url) {
工厂的模块,它可以查询RSS提要,解析文章中的标题,并返回一个承诺,该承诺将解析标题字符串数组
以下是模块代码:
angular.module('rss', [])
.factory('rssService', ['$http', '$q', '$rootScope', function($http, $q,
$rootScope) {
return function(url) {
this.getTitles = function() {
var deferred = $q.defer(),
titles = [];
$http({
method: 'GET',
url: url
}).success(function(data) {
$(data).find('title').each(function(index, item) {
var title = $(item).text();
console.log(title);
titles.push(title);
});
deferred.resolve(titles);
});
return deferred.promise;
};
};
}])
;
这是我的Jasmine单元测试代码:
beforeEach(module('rss'));
describe("RSS Module", function() {
var rssService, $httpBackend;
beforeEach(inject(function(_rssService_, _$httpBackend_) {
rssService = new _rssService_('/rss');
$httpBackend = _$httpBackend_;
}));
it("Can parse an RSS Feed and get the titles", function(done) {
$httpBackend.expectGET('/rss').respond(mockRSS); // mock data declared earlier
var titles = rssService.getTitles();
$httpBackend.flush();
titles.then(function(data) {
console.log("Done!");
expect(data.length).toBe(17);
done();
});
});
});
运行单元测试时,出现以下错误:
错误:超时-在jasmine指定的超时时间内未调用异步回调。默认超时时间间隔。
这是因为,虽然我的承诺得到了解决,但它没有调用$apply
更改。我读到的解决方案是在我解决承诺后,注入$rootScope
并调用$rootScope.$apply()
。但这样做会导致以下错误
Error: [$rootScope:inprog] $digest already in progress
这是因为我在success()
块内调用$rootScope.$apply()
,所以已经发生了一个摘要
在单元测试之外(使用实际的RSS提要),代码运行良好。控制器的作用域处理摘要,并按预期工作
有什么办法可以解决这个问题吗?我找到的解决方案是将刷新$httpBackend
的代码行移到代码后面,以声明然后块。发生的事情是GET请求被刷新,我的承诺将在我给它一个然后之前被声明和解决
新的单元测试代码如下所示:
it("Can parse an RSS Feed and get the titles", function(done) {
$httpBackend.expectGET('/rss').respond(mockRSS);
var titles = rssService.getTitles();
titles.then(function(data) {
console.log("Done!");
expect(data.length).toBe(17);
done();
});
$httpBackend.flush();
});
我不需要更改任何实际的模块代码,但如果有更好的方法使用链接承诺来编写它,我会很感兴趣。非常欢迎评论或其他答案。我找到的解决方案是将刷新$httpBackend
的代码行移动到代码后面,以声明然后块。发生的事情是GET请求被刷新,我的承诺将在我给它一个然后之前被声明和解决
新的单元测试代码如下所示:
it("Can parse an RSS Feed and get the titles", function(done) {
$httpBackend.expectGET('/rss').respond(mockRSS);
var titles = rssService.getTitles();
titles.then(function(data) {
console.log("Done!");
expect(data.length).toBe(17);
done();
});
$httpBackend.flush();
});
我不需要更改任何实际的模块代码,但如果有更好的方法使用链接承诺来编写它,我会很感兴趣。非常欢迎评论或补充答案。您的答案关于flush
和then
的顺序是正确的。flush
应该在定义回调之后进行
此外,当您从$httpBackend或$timeout使用flush
时,无需使用done
:测试将变得同步。因此,测试可以是:
it("Can parse an RSS Feed and get the titles", function() {
$httpBackend.expectGET('/rss').respond(mockRSS); // mock data declared earlier
rssService.getTitles().then(function(data) {
expect(data.length).toBe(17);
});
$httpBackend.flush();
});
另外,我认为这与您的问题没有直接关系,但是当您想要对$http
的结果进行后期处理时,通常不需要手动创建承诺。使用$http
的then
回调,而不是自定义success
,您可以使用标准承诺链接:
this.getTitles = function() {
return $http({
method: 'GET',
url: url
}).then(function(response) {
var derivedData = {length: response.data.length};
return derivedData;
});
};
您可以在上看到这一点。您的答案是正确的,关于刷新的顺序
和然后
。flush
应该在定义回调之后进行
此外,当您从$httpBackend或$timeout使用flush
时,无需使用done
:测试将变得同步。因此,测试可以是:
it("Can parse an RSS Feed and get the titles", function() {
$httpBackend.expectGET('/rss').respond(mockRSS); // mock data declared earlier
rssService.getTitles().then(function(data) {
expect(data.length).toBe(17);
});
$httpBackend.flush();
});
另外,我认为这与您的问题没有直接关系,但是当您想要对$http
的结果进行后期处理时,通常不需要手动创建承诺。使用$http
的then
回调,而不是自定义success
,您可以使用标准承诺链接:
this.getTitles = function() {
return $http({
method: 'GET',
url: url
}).then(function(response) {
var derivedData = {length: response.data.length};
return derivedData;
});
};
你可以在。Hi@austin上看到这一点,为了帮助将来有同样问题的人,最好删除这个问题,然后问一个新问题,并提供帮助人们回答问题所需的最少信息。(假设一个类似的问题还没有被问到和回答。)我对一个直接的资源也有同样的问题。$save(callback)
当我调用$httpBackend.flush()
时,它给出了已经在进行中的$digest
,但当我在资源.get()
之后执行时就没有了。改变顺序没有帮助。有什么想法吗?嗨,奥斯汀,为了在将来帮助有同样问题的人,最好删除这个问题,然后问一个新问题,并提供帮助人们回答问题所需的最少信息。(假设一个类似的问题还没有被问到和回答。)我对一个直接的资源也有同样的问题。$save(callback)
当我调用$httpBackend.flush()
时,它给出了已经在进行中的$digest
,但当我在资源.get()
之后执行时就没有了。改变顺序没有帮助。有什么想法吗?