Jasmine mock chained methods with Karma and Angula

2019-01-20 17:11发布

问题:

I want to mock angular.element. And I want to ensure angular.element has been called a certain amount of times and that anguler.element.attr has been called too.

I have the following code:

var things = $scope.getThings();

for (var i = 0; i < things.length; i++) {
  if (things[i].type == "xyz") {
    angular.element("#thing-" + things[i].id)
      .attr("foo", things[i].bar);
  };
};

In my test I have:

var things = [
  {
    id: 1,
    type: "xyz",
    bar: 10
  },
  {
    id: 2,
    type: "abc",
    bar: 33
  }
];

spyOn($rootScope, "getThings").and.returnValue(things);
spyOn(angular, "element").and.returnValue();

$rootScope.doThings(); // call controller method

expect(angular.element.calls.count()).toBe(1);

But it gives the following error:

TypeError: undefined is not an object (evaluating 'angular.element("#thing-" + things[i].id).attr')

I also want my test to have something like:

expect(angular.element.attr.calls.count()).toBe(1);
expect(angular.element.attr).tohaveBeenCalledWith("foo", things[0].bar);

回答1:

The way chained methods should be spied or mocked solely depends on how they are defined on the spied objects.

In the case of Angular jqLite, or in your case, jQuery (both are transparently served through angular.element facade) chained methods are defined on constructor prototype, which is exposed on factory function as angular.element.prototype or jQuery.prototype (angular.element === jQuery when jQuery is loaded).

In order to spy on both angular.element and angular.element(...).attr it should be:

spyOn(angular, 'element').and.callThrough();
spyOn(angular.element.prototype, 'attr').and.callThrough();
...
expect(angular.element).toHaveBeenCalled();
expect(angular.element.prototype.attr).toHaveBeenCalled();

callThrough is important in this case because otherwise the whole chain should be stubbed manually.



回答2:

try to add this code:

var spy;

beforeEach(function() {
    spy = spyOn(angular, 'element').....
});

afterEach(function() {
    spy.andCallThrough();
});

you can find more in: Jasmin docs