1

我开始为我的 Angular 应用程序编写单元测试。但是在我看来,我使用了很多样板代码来初始化和测试控制器。

在此单元测试中,我想测试在执行函数时是否将范围中的模型发送到 Api。

为此,我需要 20 行代码。这使得编写只做一件事的单元测试很不方便。您对将代码大小缩小到更小的块有任何提示吗?

这是我当前的单元测试:

'use strict';

describe('controllers', function(){
    beforeEach(module('kronos'));

    describe('CustomerSignupCtrl', function() {
        it('should send customer to Api on submit', inject(function($controller) {
            var scope = {};
            var $location = {};
            var Api = {
                signupCustomer: function(customer) {
                    expect(customer).toEqual({attrs: "customerdata"});
                    return {
                        success: function() { return this; },
                        error: function() { return this; }
                    };
                }
            };
            var ctrl = $controller('CustomerSignupCtrl', {
                $scope: scope,
                $location: location,
                Api: Api});
            scope.customer = {attrs: "customerdata"};
            scope.signup();
        }));
    });
});

我特别不喜欢的是以下几点

  • 我需要初始化所有依赖项,我是否使用它们并不重要
  • Api 返回一个我只需要的承诺,因为控制器正在期待这个承诺
  • 我需要初始化控制器。

如何使这段代码更短、更明确?

编辑:我刚刚注意到我可以忽略$location此单元测试的服务。很棒的 Edit2:我发现了angular-app,它是一个很好的实践示例应用程序。在那里你可以找到jasmine 的规格,写得非常好。

4

2 回答 2

0

你不能缩短你的代码。初始化、模拟和断言之类的事情必须在某个地方完成。但是您可以通过解耦初始化和测试代码来提高代码的可读性。像这样的东西:

describe('CustomerSignupCtrl', function(){

  var controller, scope, location, api;

  beforeEach(module('kronos'));

  // initialization
  beforeEach(inject(function($controller, $rootScope, $location, Api){
    scope = $rootScope.$new();
    location = $location;
    api = Api;
    controller = $controller('CustomerSignupCtrl', {
      $scope: scope, $location: location, Api: api});
  }));

  // test
  it('should send customer to Api on submit', function() {
    scope.customer = {attrs: "customerdata"};
    spyOn(api,'signupCustomer').andCallFake(function(customer) {    
      return {
        success: function() { return this; },
        error: function() { return this; }
      };
    });
    scope.signup();
    expect(api.signupCustomer).toHaveBeenCalledWith(scope.customer);
  });

});
于 2013-09-20T23:07:56.970 回答
0

在您的描述范围内使用另一个 beforeEach 方法来设置范围、$location、控制器等,然后在您的测试中根据需要更改它们。Js 是动态的,所以一切都应该没问题。

您还可以将您设置的每个对象提取到一个函数中,以便您可以在需要时在测试中重新初始化它们。

describe('controllers', function(){
beforeEach(module('kronos'));

describe('CustomerSignupCtrl', function() {
    var controller, scope, $location, Api;

    beforeEach(function(){
        scope = {};
        $location = {};
        Api = {
            signupCustomer: function(customer) {
                expect(customer).toEqual({attrs: "customerdata"});
                return {
                    success: function() { return this; },
                    error: function() { return this; }
                };
            }
        };

        controller = makeController();
    })

    function makeController(){
        inject(function($controller){
            controller = $controller('CustomerSignupCtrl', {
            $scope: scope,
            $location: location,
            Api: Api});
        });
    }

    it('should send customer to Api on submit', function() {
        scope.customer = {attrs: "customerdata"};
        scope.signup();
    });
});

});

于 2013-09-20T09:51:03.400 回答