【问题标题】:How to properly test an AngularJS Controller Function如何正确测试 AngularJS 控制器功能
【发布时间】:2014-10-14 15:30:21
【问题描述】:

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

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

$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() 函数在后台做了什么,而且这似乎是不正确的。

【问题讨论】:

  • 是的,你可以模拟出peopleNotesSrv 服务并监视deletNote 方法...

标签: javascript angularjs jasmine


【解决方案1】:

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

控制器测试

  1. 开始编写一个预期存在 deleteClick 的测试。
  2. 期望 deleteClick 设置加载状态(检查处理 = true)
  3. 测试服务是否注入到控制器(peopleNotesSrv)
  4. 检查 deleteClick 是否调用了服务(正如已经通过间谍提到的)
  5. 验证 $scope.noteId 和其他 $scope.params 是否存在并设置

这与控制器有关。无论是失败还是抛出错误等所有标准都应该在 Service.spec 中进行测试。由于我不详细了解您的服务,因此这里有一些示例

服务测试

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

...等等。

在控制器中测试有效性没有多大意义,因为您需要为您拥有的每个控制器进行测试。通过将服务隔离为单独的测试单元并确保它满足所有要求,您无需测试即可使用它。这有点像您永远不会测试 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 方法的任何测试。

希望这就是你的意思。

【讨论】:

  • 我们主要关心的是在服务签名更改时捕获错误。假设我们向服务调用添加了一个新参数。我们希望所有控制器测试都失败,这些测试现在在没有正确参数的情况下调用该服务。你会怎么做呢?
  • 好的,我已经更新了我之前的答案,希望这就是你的意思
【解决方案2】:

我认为答案是视情况而定。

有两种情况:


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

在这种情况下,我会保持这个测试不变,或者围绕 $scope.deleteClick() 的特定功能检查更多内容,例如是否有任何观察者在 $scope.processing 上执行任何特定于.deleteClick() 调用。


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

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


在我看来,你真的应该建立测试并尽量不要在多个地方测试同一件事,因为这会增加额外的工作,并且如果你认为,“我可以测试这个特定的从调用它的特定函数调用它的情况。”

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

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

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

我假设它是我的答案的单元测试,如果它是端到端测试,那么您可能希望继续跟踪函数调用以验证一切是否按预期进行。

这里有一些关于单元测试、端到端测试的链接,以及一篇关于两者和 Angular 的非常好的文章。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2020-01-05
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 2023-03-14
    相关资源
    最近更新 更多