Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 嵌套的AngularJS承诺不会在单元测试中工作_Javascript_Angularjs_Unit Testing_Jasmine - Fatal编程技术网

Javascript 嵌套的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) {

我正在学习如何使用AngularJS承诺,我在为它们编写单元测试时遇到了问题。我编写了一个带有
工厂的模块,它可以查询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()
之后执行时就没有了。改变顺序没有帮助。有什么想法吗?