使用andCallThrough()后,在每个测试用例后重置broadcast()

时间:2015-01-27 13:54:48

标签: angularjs unit-testing karma-jasmine

我在每个测试用例之后使用下面的代码重置$broadcast,但似乎$rootScope.$broadcast.reset();无法正常运行,因为测试后应该返回1,但它返回6。 / p>

似乎原因是andCallThrough(),因为在我使用它之前没有andCallThrough()函数,但是在一些重构之后它给了我TypeError: Cannot read property 'defaultPrevented' of undefined的错误,所以我不得不使用防止这种错误。

问题是如何在使用broadcast时重置andCallThrough,还是有其他更精确的方法?

beforeEach(function() {
   spyOn($http, 'post');
   spyOn($rootScope, '$broadcast').andCallThrough();
});

afterEach(function() {
   $http.post.reset();
   $rootScope.$broadcast.reset();
});

it('should make a POST request to API endpoint', function() {
   $http.post.andCallThrough();
   var response = { id: '123', role: 'employee', email: 'user@email.com', username: 'someUsername' };
   $httpBackend.expectPOST(apiUrl + 'login').respond(response);
   service.login();
   $httpBackend.flush();
   $timeout.flush();
   expect($rootScope.$broadcast.callCount).toBe(1);
   expect($rootScope.$broadcast).toHaveBeenCalledWith(AUTH_EVENTS.loginSuccess, response);
});

3 个答案:

答案 0 :(得分:4)

经过长时间的调查,在这种情况下事情是如何运作的,最后通过了测试并且解决方案是当前的:

问题不在于重置广播()或在使用andCallThrough()之后在每个测试用例之后调用reset方法。 问题是$rootScope.$broadcast.andCallThrough();由其他事件触发,.callCount()函数返回6,这基本上意味着$broadcast间谍被调用了6次。在我的情况下,我只对AUTH_EVENTS.loginSuccess事件感兴趣,并确保它只播放一次。

expect($rootScope.$broadcast.callCount).toBe(1);
expect($rootScope.$broadcast).toHaveBeenCalledWith(AUTH_EVENTS.loginSuccess, response);

因此挖掘$rootScope.$broadcast.calls的方法给了我所有调用的数组,从中可以检索出上述两个调用。因此,解决方案是:

it('should make a POST request to API endpoint', function() {
  $http.post.andCallThrough();
  var response = { id: '123', role: 'employee', email: 'user@email.com', username: 'someUsername' };
  $httpBackend.expectPOST(apiUrl + 'login').respond(response);
  service.login();
  $httpBackend.flush();
  $timeout.flush();

  var loginSuccessTriggerCount = _($rootScope.$broadcast.calls)
    .chain()
    .map(function getFirstArgument(call) {
      return call.args[0];
    })
    .filter(function onlyLoginSuccess(eventName) {
      return eventName === AUTH_EVENTS.loginSuccess;
    })
    .value().length;

  expect(loginSuccessTriggerCount).toBe(1);
});

答案 1 :(得分:2)

一种不同的方法。这是一个类似咖啡的伪代码。如果某些表达方式不明确,请在评论中提问。应该说这不是你问题的直接答案。只是一种方法,如何以另一种方式做类似的事情。

使用更纯净的状态进行测试

让我们介绍一个可以处理广播间谍的变量。我使用纯间谍,因为当我们处理方法覆盖或混合时,spyOn在某些繁琐的情况下可能会有效。

rootScope$broadcastSpy = jasmine.createSpy()

我们需要将原始实现与存根交换,后者将处理间谍及其状态。通常的存根定义说它是一个没有自己逻辑的实体。所以,我们是以纯粹的方式做到这一点,而不是在这里提出任何逻辑。只有一个标记(由间谍代表)。

beforeEach module ($provide) ->
  $provide.value '$rootScope',
     $broadcast: rootScope$broadcastSpy

  return

当然,我们需要在需要重置间谍时谈谈茉莉。

beforeEach ->
  rootScope$broadcastSpy.reset()

让我们定义测试范围,我们将准备所有需要的服务,并以声明的方式将它们放到上下文中(即this)。

instance = (fnAsserts) ->
  inject (loginService, $httpBackend, $timeout, apiUrl, AUTH_EVENTS) ->
    fnAsserts.call
      service: loginService
      apiUrl: apiUrl
      AUTH_EVENTS: AUTH_EVENTS
      '$httpBackend': $httpBackend
      '$timeout': $timeout

让我们以黑盒方式编写测试。我们只是设置初始状态,然后开始操作,最后让我们检查标记是否有变化。

it 'should make a POST request to API endpoint', instance ->
  # Given
  response = { id: '123', role: 'employee', email: 'user@email.com', username: 'someUsername' }
  @$httpBackend.expectPOST(@apiUrl + 'login').respond(response)

  # When
  @service.login()

  # Then
  @$httpBackend.flush()
  @$timeout.flush()
  expect(rootScope$broadcastSpy.callCount).toBe(1)
  expect(rootScope$broadcastSpy).toHaveBeenCalledWith(@AUTH_EVENTS.loginSuccess, response)

如您所见,我们可以链接实例定义,向它们添加更多信息并以更纯粹的方式编写每个测试,因为我们在单个位置查看状态,范围,模拟和其他所需的内容。我们仅对标记使用闭包,并保证最小化副作用。

答案 2 :(得分:0)

我最后在这里讨论问题的第一部分

  

重置广播()

这对我有用:

$rootScope.$broadcast.calls.reset()