如何使用Jasmine测试Angular App的服务

时间:2016-09-16 08:08:43

标签: angularjs angularjs-scope jasmine karma-jasmine

我正在练习Angular和自动化测试。

我制作了一个简单的应用来告诉用户他们的密码强度。最初一切都在PasswordController中并使用了$ scope。然后我将逻辑移动到服务中。控制器和服务目前是:

myApp.controller("PasswordController", function ($scope, PasswordService) {

  $scope.users = PasswordService.list();

  $scope.saveUser = function () {
    PasswordService.save($scope.newUser);
    $scope.newUser = {};
  }

  $scope.delete = function (id) {
    PasswordService.delete(id);
    if($scope.newUser.id == id){
      $scope.newUser = {};
    }
  }

  $scope.edit = function(id) {
    $scope.newUser = angular.copy(PasswordService.get(id));
  }
});

myApp.service('PasswordService', function() {

  //user with unique id
  var uid = 1;

  //user array
  var users = [
    {
      id: 0,
      'name': 'a_test_name',
      'password': 'a_test_pw',
      'pwStrength': 'medium'
    }
  ];

  this.save = function(user) {
    if (user.id == null) {
      user.id = uid++;
      this.grade(user);
      users.push(user);
    }
    else {
      for (i in users) {
        if (users[i].id == user.id) {
          users[i] = user;
          this.grade(user);
        }
      }
    }
  }

  this.get = function(id) {
    for (i in users) {
      if (users[i].id == id) {
        return users[i];
      }
    }
  }

  this.delete = function(id) {
    for (i in users) {
      if (users[i].id == id) {
        users.splice(i, 1);
      }
    }
  }

  this.list = function () {
    return users;
  }

  this.grade = function(user) { 
    var size = user.password.length;
    if (size > 8) {
      user.pwStrength = "strong";
    }
    else if (size > 3) {
      user.pwStrength = "medium";
    }
    else {
      user.pwStrength = "weak";
    }
  }
});

当所有内容都在PasswordController中时,我的测试用例一直在工作,但我无法弄清楚如何设置测试用例以使用该服务。这是没有修改的测试代码:(因此一些变量名称不完全对齐)

describe('PasswordController', function() {
  beforeEach(module('myApp'));

  var $controller;
  var $scope;
  var controller;

  beforeEach(inject(function(_$controller_){
    // The injector unwraps the underscores (_) from around the parameter names when matching
    $controller = _$controller_;
  }));

  describe("$scope.grade", function() {

    beforeEach(function() {
      $scope = {};
      controller = $controller("PasswordController", { $scope: $scope });
    });

    it("method should exist", function() {
      expect($scope.grade).toBeDefined();
    });

    it('sets the strength to "strong" if the password length is >8 chars', function() {
      $scope.password = 'longerthaneightchars';
      $scope.grade();
      expect($scope.strength).toEqual('strong');
    });

    it('strength to "medium" if the password length is >8 chars && <3 chars', function() {
      $scope.password = 'between';
      $scope.grade();
      expect($scope.strength).toEqual('medium');
    });

    it('sets the strength to "weak" if the password length <3 chars', function() {
      $scope.password = 'a';
      $scope.grade();
      expect($scope.strength).toEqual('weak');
    });
  });
});

我无法弄清楚如何将测试用例转换为使用PasswordService。我试图将PasswordService注入控制器并尝试使用spyOn来模拟服务并以这种方式使用它,但到目前为止还没有运气。

  beforeEach(module('myApp', function ($provide) {
    PasswordService = jasmine.createSpyObj("PasswordService", ["save" ...]);

controller = $controller("PasswordController", { $scope: $scope, PasswordSerice: PasswordService });

赞赏任何教程链接的提示。非常感谢。

1 个答案:

答案 0 :(得分:1)

由于你将控制器和服务分开(这可能是一件好事),现在分开测试也是有意义的。

您应该为控制器创建一个测试。测试仅针对控制器内部的方法(保存,删除和编辑)。

然后,您将为服务创建另一个测试,该测试将涵盖服务中的方法。

我将为这两种方案创建一个示例示例来帮助您:

describe('PasswordController Specification', function() {
  beforeEach(module('myApp'));

  var myPasswordController;

  beforeEach(inject(function(_$controller_, $rootScope){
    myPasswordController= _$controller_('PasswordController', {
         $scope: $rootScope.$new();
    });
  }));

  it("Should save the user", inject(function(PasswordService) {
    //setup
    var myFakeUser = {id: 1, name: 'whatever'};
    myPasswordController.newUser = myFakeUser;

    /*
    Here I am spying the save method. This is necessary to check if it was 
    called. It will actually replace the original implementation with a 
    mocked empty function. And this is right because the controller  
    shouldn't if the service is doing what is supposed to. This is up for 
    the service test.
    */
    spyOn(PasswordService, 'save');

    //action
    myPasswordController.saveUser(myMockedUser);

    //assertion
    expect(PasswordService.save).toHaveBeenCalled();
    expect(myPasswordController.newUser).toBe({});
  }));
});

服务测试:

describe('PasswordService Specification', function() {
  beforeEach(module('myApp'));

  var myPasswordService;

  beforeEach(inject(function(PasswordService){
    myPasswordService = PasswordService;
  }));

  it("Should save new user", inject(function() {
    //setup
    var numberOfUsers= myPasswordService.list().length;
    var newUser = {
        'name': 'a_test_name',
        'password': 'a_test_pw',
        'pwStrength': 'medium'
    }
    /*
    Here I am spying the grade method. This is necessary to check if it was 
    called. It will actually replace the original implementation with a 
    mocked empty function. And this is right because we are only concerned
    about the save method now.
    */
    spyOn(myPasswordService, 'grade');

    //action
    myPasswordService.save(newUser);

    //assertion
    expect(myPasswordService.grade).toHaveBeenCalled();
    expect(myPasswordService.list().length).toBe(numberOfUsers + 1);
  }));
});

您还可以测试其他一些内容,但我希望这能为您提供开始工作的洞察力