使用 Karma 和 Jasmine 进行 AngularJS 单元测试
⚡ 智能摘要
使用 Karma 和 Jasmine 进行 AngularJS 单元测试,可以隔离地验证控制器、指令和服务。Karma 在真实的浏览器中运行测试用例,Jasmine 提供断言语法,而 angular-mocks 则无需运行服务器即可注入依赖项。

AngularJS最出色的特性之一是 测试与验证 方面。当开发人员在 Google 在开发 AngularJS 的过程中,他们始终牢记测试的重要性,并确保整个 AngularJS 框架都是可测试的。
在 AngularJS 中,测试通常使用 Karma(框架)进行。虽然不用 Karma 也可以进行 AngularJS 测试,但 Karma 框架在测试 AngularJS 代码方面功能强大,因此使用它更为合理。
⚠️ 版本说明: AngularJS(1.x 版本)已于 2021 年 12 月 31 日正式停止维护,ProtracTor 已于 2023 年 8 月停止维护。本页所示的工作流程仍然适用于维护现有的 AngularJS 1.x 应用程序。新项目应使用最新版本的 Angular 以及当前受支持的运行器。
Karma框架介绍与安装
业力是 测试自动化工具 由 AngularJS 团队创建 Google使用 Karma 的第一步是安装 Karma。Karma 可以通过 npm 安装(npm 是一个包管理器,用于在本地计算机上轻松安装模块)。
安装 Karma
通过 npm 安装 Karma 分为两个步骤。
步骤1) 在命令行中执行下面一行
npm install karma karma-chrome-launcher karma-jasmine --save-dev
其中,
- npm 是 Node 包管理器的命令行实用程序,用于在任何机器上安装自定义模块。它随附于…… Node.js.
- install 参数指示 npm 命令行实用程序需要安装。
- 命令行中指定了 3 个与 karma 协同工作所需的库。
- karma 是用于测试目的的核心库。
- karma-chrome-launcher 是一个单独的库,可以使 karma 命令被 chrome 浏览器识别。
- karma-jasmine – 这将安装 jasmine,它是 Karma 的依赖框架。
步骤2) 下一步是安装 karma 命令行实用程序。这是执行 karma 行命令所必需的。karma 行实用程序将用于初始化 karma 环境以进行测试。
要安装命令行实用程序,请在命令行中执行以下命令
npm install -g karma-cli
- karma-cli 用于安装 Karma 的命令行界面,用户可以通过该界面在命令行中输入 Karma 命令。-g 标志会将其全局安装,以便用户可以从任何文件夹访问 karma 命令。
Karma 框架的配置
下一步是配置 karma,可以通过以下命令完成
karma init
执行完上述步骤后,karma 将创建一个 karma.conf.js 文件。该文件可能看起来像下面的代码片段
files: [ 'node_modules/angular/angular.js', 'node_modules/angular-mocks/angular-mocks.js', 'lib/app.js', 'tests/*.js' ]
上述配置文件告诉 karma 运行时引擎以下内容
- 角.js – 这告诉 Karma,你的应用程序依赖于 AngularJS 中的核心模块。
- angular-mocks.js – 这告诉 Karma 使用 angular-mocks.js 文件中的 AngularJS 单元测试功能。
- 所有主要应用程序或业务逻辑文件都存在于应用程序的 lib 文件夹中。
- 测试文件夹将包含所有单元测试。
💡提示: 文件数组中的顺序很重要。angular.js 必须在 angular-mocks.js 之前加载,并且两者都必须在应用程序代码之前加载,否则在运行规范时注入器将不可用。
要检查 karma 是否正常运行,请创建一个名为 Sample.js 的文件,输入以下代码并将其放在测试目录中。
describe('Sample test', function() { it('Condition is true', function() { expect('AngularJS').toBe('AngularJS'); }); });
上述代码有以下几个方面
- describe 函数用于对测试进行描述。在我们的例子中,我们为测试提供了“示例测试”的描述。
- “it”函数用于为测试命名。在我们的例子中,我们将测试命名为“条件为真”。测试的名称需要有意义。
- 'expect' 和 'toBe' 关键字的组合表明测试结果的预期值和实际值。如果实际值和预期值相同,则测试将通过,否则将失败。
在命令提示符下执行以下行时,它将执行上述测试文件
karma start
以下输出取自 IDE Webstorm 其中执行了上述步骤。
- 输出结果在 Karma 浏览器中 Webstorm. 该窗口显示在 karma 框架中定义的所有测试的执行情况。
- 这里您可以看到执行的测试的描述是“示例测试”。
- 接下来,您可以看到名为“条件为真”的测试本身被执行。
- 请注意,所有测试旁边都有绿色的“Ok”图标,表示所有测试都已通过。
AngularJS 单元测试中的 Jasmine 是什么?
Karma 决定测试的运行位置,而 Jasmine 决定测试的编写方式。Jasmine 是一个行为驱动的规范框架,它提供了本页所有示例中使用的词汇表,因此 karma-jasmine 适配器会与运行器一起安装。
茉莉花贡献了四个组成部分:
- 描述(): 将相关规范分组到一个套件中,并接受被测单元的易读描述。
- 它(): 声明一个单独的规范。名称应说明要验证的行为,而不是要调用的方法。
- expect() 带有匹配器: 将实际值与预期值进行比较。常用的匹配器包括 toBe、toEqual、toContain 和 toBeDefined。
- beforeEach() 和 afterEach(): 在每个规范执行之前和之后运行设置和拆卸程序,这样可以保证每个测试的独立性。
Jasmine 还提供了间谍(spy)功能。间谍会将实际函数替换为录制的替代函数,这样就可以在不调用实时 HTTP 端点的情况下测试控制器。由于 Jasmine 不需要 DOM 和自身的断言库,因此 Karma 和 Jasmine 的组合仍然是运行 AngularJS 最轻量级的方式。 Java脚本单元测试.
测试 AngularJS 控制器
karma 测试框架还具有端到端测试控制器的功能。这包括测试控制器中使用的 $scope 对象。
让我们看一个例子来了解如何实现这一点。
在我们的例子中,
我们首先需要定义一个控制器。该控制器将执行以下步骤
- 创建一个 ID 变量并为其赋值 5。
- 将 ID 变量分配给 $scope 对象。
我们的测试将测试该控制器的存在,并测试 $scope 对象的 ID 变量是否设置为 5。
首先,我们需要确保满足以下先决条件
通过 npm 安装 angular-mocks 库。这可以通过在命令提示符中执行以下命令来完成。
npm install angular-mocks --save-dev
接下来是修改 karma.conf.js 文件,以确保测试中包含正确的文件。下面的部分仅显示了需要修改的 karma.conf.js 文件部分
files: ['lib/angular.js', 'lib/angular-mocks.js', 'lib/index.js', 'test/*.js']
- “文件”参数基本上告诉 Karma 运行测试所需的所有文件。
- 运行 AngularJS 单元测试需要 angular.js 和 angular-mocks.js 文件。
- index.js 文件将包含控制器的代码
- 测试文件夹将包含我们所有的 AngularJS 测试
以下是我们的 AngularJS 代码,它将以 Index.js 文件的形式存储在我们应用程序的测试文件夹中。
下面的代码只做了以下事情
- 创建 AngularJS 模块 名为sampleApp
- 创建一个名为 AngularJSController 的控制器
- 创建一个名为 ID 的变量,赋予其值 5,并将其分配给 $scope 对象
var sampleApp = angular.module('sampleApp', []); sampleApp.controller('AngularJSController', function($scope) { $scope.ID = 5; });
一旦上述代码成功执行,下一步就是创建一个 测试用例 以确保代码已正确编写和执行。
我们的测试代码如下所示。
代码将放在一个名为 ControllerTest.js 的单独文件中,该文件将放在测试文件夹中。以下代码仅执行以下关键操作
- beforeEach 函数——此函数用于在测试运行之前加载名为“sampleApp”的 AngularJS 模块。请注意,这是 index.js 文件中的模块名称。
- `$controller` 对象是为 `AngularJSController` 控制器创建的模拟对象,该控制器定义在 `index.js` 文件中。在任何类型的单元测试中,模拟对象都代表一个用于测试的虚拟对象。这个模拟对象将模拟控制器的行为。
- beforeEach(inject(function(_$controller_) – 这用于在我们的测试中注入模拟对象,以便它表现得像实际的控制器。
- var $scope = {}; 这是为 $scope 对象创建的模拟对象。
- `var controller = $controller('AngularJSController', { $scope: $scope });` – 这里我们检查是否存在名为“AngularJSController”的控制器。同时,我们将 Index.js 文件中控制器的 `$scope` 对象中的所有变量赋值给测试文件中的 `$scope` 对象。
- 最后,我们将 $scope.ID 与 5 进行比较
describe('AngularJSController', function() { beforeEach(module('sampleApp')); var $controller; beforeEach(inject(function(_$controller_){ $controller = _$controller_; })); describe('$scope.ID', function() { it('Check the scope object', function() { var $scope = {}; var controller = $controller('AngularJSController', { $scope: $scope }); expect($scope.ID).toEqual(5); }); }); });
上述测试将在 karma 浏览器中运行,并给出与上一主题中显示的相同的通过结果。
测试 AngularJS 指令
Karma 测试框架还具备测试自定义指令的功能。这包括模板。URL在自定义指令中使用的。
让我们看一个例子来了解如何实现这一点。
在我们的示例中,我们将首先定义一个自定义指令,该指令执行以下操作
- 创建一个名为 sampleApp 的 AngularJS 模块
- 创建一个名为 guru99 的自定义指令
- 创建一个函数,返回一个带有标题标签的模板,该标签显示文本“这是 AngularJS 测试”。
var sampleApp = angular.module('sampleApp', []); sampleApp.directive('guru99', function () { return { restrict: 'E', replace: true, template: '<h1>This is AngularJS Testing</h1>' }; });
一旦上述代码成功执行,下一步就是创建一个测试用例,以确保代码已正确编写和执行。我们的测试代码如下所示
代码将位于名为 DirectiveTest.js 的单独文件中,该文件将放置在测试文件夹中。以下代码仅执行以下关键操作
- beforeEach 函数 – 此函数用于在测试运行之前加载名为“sampleApp”的 AngularJS 模块。
- `$compile` 服务用于编译指令。此服务是必需的,必须声明,以便 AngularJS 可以使用它来编译我们的自定义指令。
- `$rootScope` 是任何 AngularJS 应用程序的主要作用域。我们在前面的章节中已经见过控制器的 `$scope` 对象。实际上,`$scope` 对象是 `$rootScope` 对象的子对象。之所以在这里声明它,是因为我们通过自定义指令修改了 DOM 中的一个实际 HTML 标签。因此,我们需要使用 `$rootScope` 服务,它会监听并检测 HTML 文档中发生的任何更改。
- var element = $compile(“ ”($rootScope) – 这用于检查我们的指令是否按预期注入。该指令注册的名称为驼峰式命名法的 guru99,因此 AngularJS 会匹配小写元素。在标记中。因此,此语句用于进行该检查。
- expect(element.html()).toContain(“This is AngularJS Testing”) – 这用于指示 expect 函数应该找到包含 innerHTML 文本“This is AngularJS Testing”的元素(在我们的例子中是 h1 标签)。
describe('Unit testing directives', function() { var $compile, $rootScope; beforeEach(module('sampleApp')); beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); it('Check the directive', function() { // Compile a piece of HTML containing the directive var element = $compile("<guru99></guru99>")($rootScope); $rootScope.$digest(); expect(element.html()).toContain("This is AngularJS Testing"); }); });
上述测试将在 karma 浏览器中运行,并给出与上一主题中显示的相同的通过结果。
对 AngularJS 应用进行端到端测试
业力测试框架以及一个名为 Pro 的框架。tracTor 具备对 Web 应用程序进行端到端测试的功能。
因此,它不仅是指令和控制器的测试,也是对 HTML 页面上可能出现的任何其他内容的测试。
让我们看一个例子来了解如何实现这一点。
在下面的示例中,我们将有一个使用 ng-repeat 指令创建数据表的 AngularJS 应用程序。
- 我们首先创建一个名为“tutorial”的变量,并一次性为其分配一些键值对。显示表格时,每个键值对都将用作数据。然后将 tutorial 变量分配给范围对象,以便可以从我们的视图中访问它。
- 对于表中的每一行数据,我们都使用 ng-repeat 指令。该指令使用变量 ptutor 遍历教程范围对象中的每个键值对。
- 最后,我们使用标签以及键值对(ptutor.Name 和 ptutor.Description)来显示表格数据。
<table> <tr ng-repeat="ptutor in tutorial"> <td>{{ ptutor.Name }}</td> <td>{{ ptutor.Description }}</td> </tr> </table> </div> <script type="text/javascript"> var app = angular.module('DemoApp', []); app.controller('DemoController', function($scope) { $scope.tutorial = [ {Name: "Controllers", Description: "Controllers in action"}, {Name: "Models", Description: "Models and binding data"}, {Name: "Directives", Description: "Flexibility of Directives"} ]; });
一旦上述代码成功执行,下一步就是创建一个测试用例,以确保代码已正确编写和执行。我们的测试代码如下所示
我们的测试实际上要测试 ng-repeat 指令并确保它包含如上例所示的 3 行数据。
首先,我们需要确保满足以下先决条件
安装专业版trac通过 npm 安装 tor 库。这可以通过在命令提示符中执行以下命令来完成。
npm install -g protractor
我们的测试代码如下所示。
代码将放在一个名为 CompleteTest.js 的单独文件中,该文件将放在测试文件夹中。下面的代码只执行以下关键操作
- 浏览器功能由专业人士提供。tractor 库,并假设我们的 AngularJS 应用程序(包含上述代码)正在我们的网站上运行。 URL – http://localhost:8080/Guru99 /
- var list = element.all(by.repeater('ptutor in tutorial')); - 这行代码实际上是在获取由代码 'ptutor in tutorial' 填充的 ng-repeat 指令。element 和 by.repeater 是 pro 提供的特殊关键字。tractor 库,允许我们获取 ng-repeat 指令的详细信息。
- expect(list.count()).toEqual(3); – 最后,我们使用 expect 函数来查看我们确实由于 ng-repeat 指令而获得了表中填充的 3 个项目。
describe('Unit testing end to end', function() { beforeEach(function() { browser.get('http://localhost:8080/Guru99/'); }); it('Check the ng directive', function() { var list = element.all(by.repeater('ptutor in tutorial')); expect(list.count()).toEqual(3); }); });
上述测试将在 karma 浏览器中运行,并给出与上一主题中显示的相同的通过结果。
业力 vs 茉莉 vs 职业tractor:主要区别
本页使用的三个工具经常被混淆,因为它们出现在同一个命令行中。如下表所示,每个工具都位于测试堆栈的不同层级。
| 参数 | 因果报应 | Jasmine | 专业版trac器 |
| 类别 | 测试运行员 | 规范框架 | 端到端自动化框架 |
| 主要工作 | 启动浏览器并报告结果 | 提供描述、说明和预期 | 在真实浏览器中运行实时应用程序 |
| 测试级别 | 单位和 积分 | 单位积分 | 端到端 |
| 需要一台正在运行的服务器 | 没有 | 没有 | 是 |
| 配置文件 | 业力配置文件 | 没有,它在运行器内部运行。 | 为tractor.conf.js |
| 当前状态 | 已弃用,仅供维护使用 | 积极维护 | 2023年8月退休 |
简而言之,Jasmine 编写测试,Karma 执行测试,而 Pro 则负责处理测试结果。tractor 将 Jasmine 的语法扩展到了完全渲染的页面。
AngularJS 单元测试最佳实践
随着应用程序的增长,养成一些简单的习惯就能使 AngularJS 套件保持快速可靠。
- 一次只测试一个单元。 在 beforeEach 中只加载被测模块,并模拟每个协作者,以便故障指向单个文件。
- 永远不要调用真正的后端。 使用 angular-mocks 中的 $httpBackend 服务返回预设响应。测试随后离线运行,并在每台机器上产生相同的结果。
- 保持规范名称具有行为性。 在报告中,“控制器初始化时返回 5”这样的名称比“test1”更易读。
- 务必彻底冲洗消化循环系统。 更改作用域数据后调用 $rootScope.$digest(),否则绑定和监视器永远不会更新,断言会因错误的原因而失败。
- 衡量覆盖范围,但要谨慎判断。 添加 karma-coverage 可以查看未测试的分支。即使覆盖率很高,但如果断言不够充分,仍然会隐藏缺陷。
- 在持续集成模式下运行该套件。 使用无头 Chrome 启动器,以便每次提交时都执行相同的规范,而不仅仅是在开发人员的机器上执行。
应用这些规则可以将上述示例从孤立的演示转化为对遗留 AngularJS 代码库的回归安全网。如需了解更全面的信息,请参阅…… AngularJS教程 系列丛书中。

