如何正确测试AngularJS控制器功能

时间:2014-10-14 15:30:21

标签: javascript angularjs jasmine

我们刚开始在AngularJS项目中实施茉莉花测试,我有一个问题:

我们想测试这个控制器功能:

$scope.deleteClick = function () {
        $scope.processing = true;
        peopleNotesSrv.deleteNote($scope.currentOperator.operatorId, $scope.noteId, $scope.deleteSuccessCallback, $scope.deleteErrorCallback);
    };

我们写了以下测试:

    it('deleteClick should pass proper parameters to peopleNoteSrv', function () {
        $controllerConstructor('PeopleNoteEditCtrl', { $scope: $scope });

        $scope.noteId = 5;

        expect(function () { $scope.deleteClick(); }).not.toThrow();
    });

此测试确保当我们调用$ scope.deleteClick()函数时,$ scope.processing设置为true,并且对peopleNotesSrv的调用不会因为参数无效而抛出任何错误。我们在单独的测试中测试两个回调函数。

我们是否应该测试是否调用了peopleNotesSrv.deleteNote函数,以便测试更明确?现在编写这个测试的方式并没有真正告诉别人deleteClick()函数在幕后做了什么,这似乎是不正确的。

2 个答案:

答案 0 :(得分:1)

问问自己,如果你使用TDD开发了它,你会做些什么。这几乎是Sam指出的方向,但这里有一些例子:

控制器测试

  1. 开始编写一个期望存在deleteClick的测试。
  2. 期望deleteClick设置加载状态(check for processing = true)
  3. 测试服务是否注入控制器(peopleNotesSrv)
  4. 检查deleteClick是否调用该服务(如已经通过间谍提到的那样)
  5. 验证$ scope.noteId和其他$ scope.params是否存在并设置
  6. 这与控制器有关。应该在Service.spec中测试所有标准是失败还是抛出错误等。由于我在这里不详细了解您的服务,所以有一些例子

    服务测试

    1. 确保deleteNote存在
    2. 检查如果提供错误的参数数量(少或多)
    3. 会发生什么
    4. 做一些积极的测试(比如你的noteId = 5)
    5. 进行一些负面测试
    6. 确保正确调用回调
    7. ......等等。

      在控制器中测试有效性并不是很有意义,因为您需要为每个控制器执行此操作。通过将服务隔离为一个单独的测试单元并确保它满足所有要求,您可以在不进行测试的情况下使用它。它有点像你从未测试jQuery功能或Angular jQLite一样,因为你只是期望它们做他们应该做的事情:)

      编辑:

      使控制器测试在服务调用

      时失败

      很容易让我们举个例子。首先,我们创建服务测试,以确保在没有提供适当数量的参数的情况下调用失败:

      describe('Service: peopleNoteSrv', function () {
      
        // load the service's module
       beforeEach(module('angularControllerServicecallApp'));
      
       // instantiate service
       var peopleNoteSrv;
       beforeEach(inject(function (_peopleNoteSrv_) {
         peopleNoteSrv = _peopleNoteSrv_;
       }));
      
       it('should throw error on false number of arguments', function () {
         expect(function() { peopleNoteSrv.deleteNote('justOneParameter'); }).toThrow();
       });
      
      });
      

      现在确保测试通过让我们在服务方法中创建错误抛出部分

      angular.module('angularControllerServicecallApp')
        .service('peopleNoteSrv', function peopleNoteSrv() {
      
          this.deleteNote = function(param1, param2, param3) {
            if(arguments.length !== 3)
              throw Error('Invalid number of arguments supplied');
            return "OK";
          };
      });
      

      现在让我们创建2个演示控制器,FirstCtrl将正确执行,但SecondCtrl应该失败

      angular.module('angularControllerServicecallApp')
        .controller('FirstCtrl', function ($scope, peopleNoteSrv) {
          $scope.doIt = function() {
            return peopleNoteSrv.deleteNote('param1', 'param2', 'param3');
          }
        });
      
      angular.module('angularControllerServicecallApp')
        .controller('SecondCtrl', function ($scope, peopleNoteSrv) {
          $scope.doIt = function() {
            return peopleNoteSrv.deleteNote('onlyOneParameter');
          }
        });
      

      作为演示的两个控制器都有以下测试:

      it('should call Service properly', function () {
        expect(scope.doIt()).toBe("OK");
      });
      

      Karma现在吐出这样的东西:

      Error: Invalid number of arguments supplied
          at [PATH]/app/scripts/services/peoplenotesrv.js:15
          at [PATH]/app/scripts/controllers/second.js:13
          at [PATH]/test/spec/controllers/second.js:20
      

      因此,您确切地知道您错过了更新SecondCtrl。当然,这应该适用于任何使用Service方法的测试。

      希望这就是你的意思。

答案 1 :(得分:0)

我认为答案取决于它。

有两种情况:


1 - 您还有一套针对peopleNotesSrv服务的测试。

在这种情况下,我会按原样保留此测试,或者检查$ scope.deleteClick()的特定功能周围的一些内容,例如,如果$ scope.processing上有任何观察者做任何特定的关于a .deleteClick()调用。


2 - 您没有对peopleNotesSrv服务的所有可能功能进行任何测试。

在这种情况下,我会编写一个更明确的测试来检查.deleteNote()是否实际执行了它的工作。


在我看来,你应该真正构建测试并尝试不在多个地方测试相同的东西,因为这增加了额外的工作,并且如果你认为,可能会在测试中产生漏洞,“我可以测试这个特定的从调用它的特定函数调用它的情况。“

如果你想在不同的地方重用deletNote()作为更大函数的一部分怎么办?那么你需要为同一个代码编写另一个测试,因为它是从不同的函数调用的。

所以我的目标是案例1,这样你就可以为该服务编写所有测试,然后相信这些测试涵盖了这个特定测试的其余部分。如果您在输入错误或未能实际删除注释时抛出错误,您应该相信其他代码可以测试它的设计测试内容。这将大大加快您的测试编写时间并增加测试涵盖所有案例的机会。它还将该服务的所有测试保存在测试代码中的相同位置。

我认为开始时还有一个很好的问题是这是一种什么样的测试?单元测试或端到端测试?

我假设它是我的答案的单元测试,如果它是一个端到端的测试,那么你可能想继续跟随函数调用来验证一切正如你所期望的那样。

以下是有关单元测试,端到端测试的一些链接,以及关于Angular和Angular的一篇非常好的文章。