在单元测试中嘲笑我自己的服务

时间:2012-11-27 20:13:21

标签: unit-testing mocking angularjs

我有一项服务,将我的其他几项服务作为依赖项。我如何模拟它进行单元测试?

myApp.factory('serviceToTest',
           ['serviceDependency',
    function(serviceDependency) {
        return function(args) {
            return cond(args) ? serviceDependency() : somethingElse();
        };
    }
]);

在上面的示例中,我想模拟serviceDependency,以便我可以验证它是否被调用。我怎么能这样做?

我可以在测试中执行以下操作:

describe("Services", function() {
    describe('serviceToTest', function() {

        myApp.factory('serviceDependency', function() {
            var timesCalled = 0;
            return function() {
                return timesCalled++;
            }
        });

        it('should do foo', inject(function(serviceToTest, serviceDependency) {
            serviceToTest(["foo", "bar", "baz"]);
            expect(serviceDependency()).to.equal(1);
        });
    });
});

这适用于需要模拟的测试,但它会影响后面所有其他测试的状态,这显然是一个问题。

3 个答案:

答案 0 :(得分:11)

如果我理解正确,您希望测试依赖于其他服务的服务并模拟每个测试的依赖关系。如果是这样,我们假设我们有car依赖于engine

var app = angular.module('plunker', [])
  .factory('car', function(engine) {
     return {
       drive : function() {
         return 'Driving: ' + engine.speed();
       }
     }
  })
  .value('engine', {
    speed : function() {
      return 'fast';
    }
  });

然后你要测试汽车并模拟引擎。有两种方法可以这样做:通过定义一个我们可以重新定义引擎的新模块:

describe('Testing a car', function() {
  var testEngine;

  beforeEach(function(){
    testEngine = {};
    angular.module('test', ['plunker']).value('engine', testEngine);
    module('test');
  });   

  it('should drive slow with a slow engine', inject(function(car) {
    testEngine.speed = function() {
      return 'slow';
    };
    expect(car.drive()).toEqual('Driving: slow');
  }));
});

在这里工作:http://plnkr.co/edit/ueXIzk?p=preview

更简单的替代方案,转发JavaScript的动态特性:

describe('Testing a car', function() {
  var testEngine;

  beforeEach(module('plunker'));
  beforeEach(inject(function(engine){
    testEngine = engine;
  }));

  it('should drive slow with a slow engine', inject(function(car) {
    testEngine.speed = function() {
      return 'slow';
    };
    expect(car.drive()).toEqual('Driving: slow');
  }));
});

http://plnkr.co/edit/tlHnsJ?p=preview

另一种选择是使用Jasmine的间谍:

describe('Testing a car', function() {
  var testEngine;

  beforeEach(module('plunker'));
  beforeEach(inject(function(engine){
    testEngine = engine;
  }));

  it('should drive slow with a slow engine', inject(function(car) {
    spyOn(testEngine, 'speed').andReturn('slow');
    expect(car.drive()).toEqual('Driving: slow');
  }));
});

http://plnkr.co/edit/K4jczI?p=preview

答案 1 :(得分:7)

我在尝试将模拟服务注入其他提供程序时遇到了同样的问题。

Pete Bacon Darwin的这篇文章让我走上正轨:https://groups.google.com/d/msg/angular/TvBknXnjRyQ/xtCDkJyqp6MJ

以下是一个示例应用程序:

angular.module('MyApplication', [])
    .service('MyServiceDependency', [function(){
        // Behaviour here.
    }])
    .factory('MyFactory', ['MyServiceDependency', function(MyServiceDependency){
        // Behaviour here.
    }]);

我们想要模拟MyServiceDependency来测试MyFactory的行为,所以我们写下这样的测试:

describe(function(){

    var MyFactory;

    beforeEach(function(){
        // 1. Include your application module for testing.
        angular.mock.module('MyApplication');

        // 2. Define a new module.
        // 3. Define a provider with the same name as the one you want to mock (in our case we want to mock 'MyServiceDependency'.
        angular.module('MyAppMock', [])
            .service('MyServiceDependency', function(){
                // Define you mock behaviour here.
            });

        // 4. Include your new mock module - this will override the providers from your original module.
        angular.mock.module('MyAppMock');

        // 5. Get an instance of the provider you want to test.
        inject(function($injector){
            // `MyFactory` will receive the mocked version of `MyServiceDependency`.
            MyFactory = $injector.get('MyFactory');
        });
    });

    it('MyFactory does something special', function(){
        MyFactory(); // Test your provider.
    });

});

答案 2 :(得分:3)

以下是我的开源项目中的一个示例:https://github.com/lucassus/mongo_browser/blob/f1faf1b89a9fc33ef4bc4eced386c30bda029efa/spec/javascripts/app/services_spec.js.coffee#L25(抱歉coffeescript)。 通常在规范内部,您必须创建并包含一个覆盖给定服务的新模块。