如何使用jasmine模拟Angular服务?

时间:2013-10-02 08:29:44

标签: unit-testing angularjs jasmine

这可能是重复的,但我在这里看了很多其他问题,他们通常会以某种方式错过我正在寻找的东西。他们主要谈论他们自己创造的服务。我能做到并做到了。我试图覆盖我的模拟注入的角度。我认为它会是相同的,但由于某些原因,当我单步执行代码时,它始终是角度$ cookieStore而不是我的模拟。

我对jasmine和angularjs的经验非常有限。我来自C#背景。我经常编写单元测试moq(C#的模拟框架)。我习惯看到这样的东西

[TestClass]
public PageControllerTests
{
    private Mock<ICookieStore> mockCookieStore;
    private PageController controller;

    [TestInitialize]
    public void SetUp()
    {
        mockCookieStore = new Mock<ICookieStore>();
        controller = new PageController(mockCookieStore.Object);
    }

    [TestMethod]
    public void GetsCarsFromCookieStore()
    {
        // Arrange
        mockCookieStore.Setup(cs => cs.Get("cars"))
                    .Return(0);

        // Act
        controller.SomeMethod();

        // Assert
        mockCookieStore.VerifyAll();
    }
}

我想模拟我在我的一个控制器中使用的$ cookieStore服务。

app.controller('PageController', ['$scope', '$cookieStore', function($scope, $cookieStore) {

    $scope.cars = $cookieStore.get('cars');

    if($scope.cars == 0) {
            // Do other logic here
            .
    }

    $scope.foo = function() {
        .
        .
    }
}]);

我想确保使用'garage'参数调用$ cookieStore.get方法。我也希望能够控制它的回馈。我希望它返回0然后我的控制器必须做一些其他的逻辑。

这是我的测试。

describe('Controller: PageController', function () {

    var controller,
        scope,
        cookieStoreSpy;

    beforeEach(function () {
        cookieStoreSpy = jasmine.createSpyObj('CookieStore', ['get']);
        cookieStoreSpy.get.andReturn(function(key) {
            switch (key) {
                case 'cars':
                    return 0;
                case 'bikes':
                    return 1;
                case 'garage':
                    return { cars: 0, bikes: 1 };
            }
        });

        module(function($provide) {
            $provide.value('$cookieStore', cookieStoreSpy);
        });
        module('App');

    });

    beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
        scope = $rootScope.$new();
        controller = $controller;
    }));

    it('Gets car from cookie', function () {
        controller('PageController', { $scope: scope });
        expect(cookieStoreSpy.get).toHaveBeenCalledWith('cars');
    });
});

4 个答案:

答案 0 :(得分:5)

这是我之前回答中讨论的解决方案。

在我的控制器中,我使用的是$ location.path和$ location.search。所以用我的模拟覆盖$ location:

locationMock = jasmine.createSpyObj('location', ['path', 'search']);
locationMock.location = "";

locationMock.path.andCallFake(function(path) {
  console.log("### Using location set");
  if (typeof path != "undefined") {
    console.log("### Setting location: " + path);
    this.location = path;
  }

  return this.location;
});

locationMock.search.andCallFake(function(query) {
  console.log("### Using location search mock");
  if (typeof query != "undefined") {
    console.log("### Setting search location: " + JSON.stringify(query));
    this.location = JSON.stringify(query);
  }

  return this.location;
});

module(function($provide) {
  $provide.value('$location', locationMock);
});

我没有在$ controller中注入任何东西。它刚刚起作用。查看日志:

  

日志:'###使用位置设置'

     

日志:'###设置位置:/ test'

     

日志:'###使用位置搜索模拟'

     

日志:'###设置搜索位置:{“限制”:“50”,“q”:“ani”,“tags”:[1,2],“category_id”:5}'

答案 1 :(得分:0)

如果你想检查参数,请监视方法

// declare the cookieStoreMock globally
var cookieStoreMock;

beforeEach(function() {
  cookieStoreMock = {};

  cookieStoreMock.get = jasmine.createSpy("cookieStore.get() spy").andCallFake(function(key) {
    switch (key) {
      case 'cars':
        return 0;
      case 'bikes':
        return 1;
      case 'garage':
        return {
          cars: 0,
          bikes: 1
        };
    }
  });

  module(function($provide) {
    $provide.value('cookieStore', cookieStoreMock);
  });
});

然后测试参数do

expect(searchServiceMock.search).toHaveBeenCalledWith('cars');

答案 2 :(得分:0)

这是一个示例https://github.com/lucassus/angular-seed/blob/81d820d06e1d00d3bae34b456c0655baa79e51f2/test/unit/controllers/products/index_ctrl_spec.coffee#L3它的coffeescript代码与mocha + sinon.js,但想法是一样的。

基本上使用以下代码段,您可以加载模块并替换其服务:

beforeEach(module("myModule", function($provide) {
  var stub = xxx; //... create a stub here
  $provide.value("myService", stub);
}));

稍后在规范中,您可以注入此存根服务并执行断言:

it("does something magical", inject(function(myService) {
  subject.foo();
  expect(myService).toHaveBeenCalledWith("bar");
}));

有关模拟和测试的更多详细信息和提示,请参阅此优秀博文:http://www.yearofmoo.com/2013/09/advanced-testing-and-debugging-in-angularjs.html

答案 3 :(得分:0)

为什么在没有修改的情况下直接使用它来模拟cookieStore?下面的代码是一个控制器的部分单元测试,它使用$ cookieStore来放置和获取cookie。如果你的控制器有一个名为“setACookie”的方法,它使用$ cookieStore.put('cookieName',cookieValue)......那么测试应该能够读取设置的值。

describe('My controller', function() {
   var $cookieStore;
   describe('MySpecificController', function() {

    beforeEach(inject(function(_$httpBackend_, $controller, _$cookieStore_) {
        $cookieStore = _$cookieStore_;
        // [...] unrelated to cookieStore
    }));

    it('should be able to reference cookies now', function () {
       scope.setACookie();
       expect($cookieStore.get('myCookieName')).toBe('setToSomething');
    });

 });