2016-09-16 92 views
0

我正在练习Angular和自动化测试。如何使用茉莉花来测试Angular App的服务

我做了一个简单的应用程序,告诉用户他们的密码强度。原来一切都在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

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

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

然后您将创建另一个服务测试,它将覆盖服务内的方法。

我会为这两种情况下创建一个示例例子来帮助你:

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); 
    })); 
}); 

有,你可以测试其他的东西,但我希望这给你洞察力开始你的工作

+0

谢谢SOOOO多!这非常有帮助。 – SuperCow

+0

在服务测试行中: beforeEach(inject(function(myPasswordService){myPowerService = myPasswordService; })); 我们是否应该将PasswordService传递给函数,因为这是服务的名称?然后设置myPasswordService = PasswordService(就你的例子而言)? – SuperCow

+0

对此没有正确答案。如果您将在许多“it”块中使用PasswordService,那么您可以拥有那个功能。这只是减少代码行数的一种方法。你只需要分析你的代码是否更有组织。 –