Ionic Framework 中的 TDD 简介





5.00/5 (1投票)
Ionic 中测试驱动开发简介
简而言之
在这篇相当长的文章中,我将向您介绍 Ionic 中的测试驱动开发(TDD)。首先,我将涵盖一些基本的理论概念,然后我们将通过几个示例来了解如何应用这些概念,首先是纯 JavaScript,最后是 Ionic。
在本教程结束时,您将清楚地了解如何在 JavaScript 和 Ionic 应用程序中开始实践 TDD。此外,在底部,您将看到我为学习 TDD 而查阅的所有资源的完整“资源转储”。
如果您有兴趣,可以在此处查看演示文稿幻灯片。
让我们回答一些棘手的问题
你们中有多少人实际测试自己的代码?别担心,这是一个反问,你们不需要举手。
好吧,如果坦白说,就我而言(因为我最近主要编写 JavaScript),直到最近,我一直在实践一种所谓的 CLTDD。当然,它代表 console.log TDD。
我们都知道我们应该做些**什么**来改善这种情况,但很多时候,我们却像这位先生一样
好了,玩笑归玩笑,让我试着强调一下为什么测试实际上可能对您有用。只需思考以下问题
- 你是否曾修复一个 Bug,却发现它破坏了系统中的另一个部分?
- 你是否曾害怕触碰一段复杂的代码,担心可能会破坏它?
- 你是否曾发现一段你很确定不再使用、应该删除的代码,但你还是把它留在那儿,以防万一?
如果这些问题的任何一个答案是肯定的,那么如果正确实践 TDD,您就会看到它所带来的价值。
什么是 TDD?
由于我们大多数人都是开发人员,我敢打赌您一定听说过**单元测试**。然而,单元测试与 TDD 并非一回事。单元测试是一种**测试类型**。TDD 是一种**编码技术**。这意味着如果您编写单元测试,您实际上并不一定会做 TDD。
TDD 是一种编写软件的方法,您**在编写应用程序代码之前先编写测试**。基本步骤是
- **红色**——编写一个测试并确保它失败
- **绿色**——编写最简单的代码以使测试通过
- **重构**——简化/重构应用程序代码,确保所有测试仍然通过
此时,您可能会想
“等等,我现在必须编写代码来测试我还没写的代码?!”
是的,你编写了更多的代码,但研究表明,**使用 TDD 进行良好的测试覆盖可以使 Bug 密度降低 40% – 80%**。
为什么要费心测试?
那么,你为什么要首先测试你的代码呢?难道你没有一个即将到来的截止日期,现在你应该把宝贵的时间花在编写测试上,而不是实际的应用程序代码上吗?
好吧,随着功能和代码库的增长,手动 QA 变得更加昂贵、耗时且容易出错。
举例来说,如果你从代码中删除一些函数,你还记得它所有潜在的副作用吗?可能不记得。但有了单元测试,你甚至不必记住。**如果你删除了其他地方有要求的东西,该单元测试就会失败,你就会知道你做错了什么**。
所以基本上,我们测试代码是为了验证它是否按照我们预期的方式运行。通过这个过程,你会发现你为自己和其他开发者提供了更好的功能文档。
此外,正如James Sinclair所说,实践 TDD 迫使人们思考,因为你必须先思考,然后编写测试。此外,它使调试更容易,编程更有趣。
关于 TDD 和单元测试的 5 个常见误解
根据Eric Elliot的说法,关于 TDD 和单元测试有 5 个常见的误解。
- TDD 太耗时了
- 你无法在了解设计之前编写测试,也无法在实现代码之前了解设计。
- 在开始编写代码之前,你必须编写所有测试
- 红、绿,总是重构?
- 一切都需要单元测试
此外,他对 TDD 中的模拟(mocking)持相当坚定的观点
这里有一个能改变你人生的建议:模拟是一种代码异味。
演示时间
好了,理论就到这里,现在让我们看看代码吧!
必备组件
为了能够学习本教程,您需要安装 Node.js。此外,您还需要通过 **npm** 全局安装以下包
我选择 Karma 作为运行测试的环境,Jasmine 用于实际测试用例,因为这些框架对我来说似乎是这项任务最可靠且使用广泛的。但是,请记住还有许多其他选项。值得一提的包括 Mocha、Chai、Sinon、Tape 等。
我想补充的是,现在(尤其是在 JavaScript 世界中),您有大量的选择。**选择一个选项并真正开始比无休止地权衡选项要好得多。**
使用 Jasmine,我们将采用行为驱动开发 (BDD) 风格来编写测试。这是 TDD 的一种变体,测试以以下形式编写
- 描述 `[事物]`
- 它应该 `[做某事]`
`[thing]` 可以是模块、类或函数。Jasmine 包含 `describe()` 和 `it()` 等内置函数,使得以这种风格编写成为可能。此外,Jasmine 还提供了一些其他很酷的功能,例如 spies,我们在这里不会介绍,但您可以从官方文档中了解更多信息。
JavaScript 演示
在此演示中,我将向您展示一个简单的分步 TDD 方法来构建一个简单的计算器库。这将是一个只有两个函数(`add` 和 `sub`)的简单文件。这不会有什么花哨之处,只是为了说明这个过程是如何进行的。
文件夹结构和依赖项
首先,我们创建一个名为 *jstdd* 的新文件夹,并在其中创建一个 *app* 文件夹
mkdir jstdd && cd jstdd && mkdir app && cd app
此外,在 *app* 文件夹中创建一个 *index.js* 文件
touch index.js
接下来,在 *jstdd* 目录中执行 `npm init`。这将为我们创建一个 *package.json* 文件,所有其他依赖项(我们很快就会安装)都将保存到其中。在 `npm init` 命令的每个问题中,您都可以安全地按 **ENTER** 键,保留默认值。
接下来,安装所有必需的依赖项
npm install karma karma-jasmine jasmine-core karma-phantomjs-launcher --save-dev
对于不太熟悉 Node 和 npm 的人,使用 `--save-dev` 开关,我们将这些依赖项保存到我们用前面提到的 `npm init` 命令创建的 *package.json* 文件中。
接下来,创建一个名为 *tests* 的新文件夹,并在其中创建一个 *index.spec.js* 文件
mkdir tests && cd tests && touch index.spec.js
设置 Karma
基本上,我们现在已经设置好了一切。但是,在我们真正开始编写测试之前,我们必须配置 Karma。因此,在应用程序的根目录(文件夹 *jstdd*)中,我们必须执行
karma init
问题的答案应该是
- 使用 Jasmine 作为测试框架。
- 不要使用 Require.js。
- 使用 PhantomJS 而不是 Chrome(使用键盘上的 **TAB** 键在选项之间切换)。这是因为我们想在控制台中运行测试。
- 当被问及源文件和测试文件时,使用 *app/*.js* 和 *tests/*.spec.js*。我们可以使用 glob 模式,这意味着星号 (*) 匹配任何内容。
- 当被问及要排除哪些文件时,只需按 **ENTER** 键跳过。
- 最后,选择**是**让 Karma 监视所有文件并在更改时运行测试。
完成此过程后,Karma 生成了 *karma.conf.js* 文件,该文件(不包含注释)应如下所示
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'app/*.js',
'tests/*.spec.js'
],
exclude: [],
preprocessors: {},
reporters: ['spec'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'],
singleRun: false,
concurrency: Infinity
});
};
最后,让我们编写一些测试
此时,我们已完成所有设置,可以开始编写测试了。我们将在 *index.spec.js* 文件中编写测试。
提醒一下,我们的目标是创建一个简单的计算器库。所以,我们从编写测试开始。
当我们使用 Jasmine 测试代码时,我们使用 Jasmine 称之为 `test suite` 的东西将测试分组在一起。我们通过调用 Jasmine 的全局 `describe` 函数来开始我们的测试套件。
所以我们将在 *index.spec.js* 文件中编写
describe ("Calculator", function (){
});
此函数接受两个参数:一个 `string` 和一个函数。`string` 用作标题,函数是实现我们测试的代码。
在此 `describe` 块中,我们将添加所谓的**规范(specs)**。在我们的 `it` 块中,我们放置了用于测试代码的期望。
例如,我们要测试的第一件事是,我们确实有一个 `add` 函数
it('should have an add function', function() {
expect(add).toBeDefined();
});
别担心语法,通过阅读 Jasmine 的文档可以轻松学习。此外,好消息是所有测试工具的语法或多或少都相似。
好的,我们已经编写了测试,但是接下来呢?我们通过运行 `karma start` 在终端中运行测试。
您应该会看到类似以下内容
我们在这里看到了什么?我们看到有一个测试失败了。那么,我们现在该怎么办?我们进入下一步,并以最简单的方式使测试通过。那么,我们该怎么做呢?我们在 *index.js* 文件中编写一个 `add` 函数
function add() {}
现在,我们的测试通过了。太棒了。我们可以重构(第三步)一些东西吗?在这个阶段很可能不行,所以我们继续前进。
那么,我们对 `add` 函数的下一个期望是什么呢?例如,我们期望如果传入数字 1 和 2,它将返回数字 3。那么我们如何编写这个测试呢?嗯,就像我们说的那样。所以
it ("should return 3 when passed 1, 2", function (){
expect(3).toEqual(add(1,2));
});
现在我们有一个失败的测试,我们去修复它。此时,我们问自己
通过这个测试的最快方法是什么?
好吧,这个问题的答案是从我们的函数中返回 3
function add(){
return 3;
}
然而,我们又一次通过了测试。
然而,假设我们想再做一个测试,我们说传入 3 和 2 时我们期望得到 5
it ("should return 5 when passed 3, 2", function (){
expect(5).toEqual(add(3,2));
});
好吧,我们可以让它通过的一种方法是检查参数并创建一些 switch cases……但是,正如你所看到的,这正在增长,而且说实话,这不是我们应该做事情的方式,所以我们重构。
所以,经验法则,第三步是重构,并确保所有测试仍然通过。
灵感迸发之际,我们(在 *index.js* 文件中)编写了
function add (a, b){
return a + b;
}
这样,我们现在有了一个通过的测试和重构后的代码。
美化输出
此时,我们所有通过的规格可能没有很好地呈现。如果您想看到这一点,可以安装
npm install karma-spec-reporter --save-dev
npm install jasmine-spec-reporter --save-dev
然后,在 *karma.conf.js* 文件中,只需将报告器更改为 `spec`,如下所示
reporters: ['spec']
现在,当我们运行 `karma start` 时,我们将得到一个漂亮的输出,例如
Calculator
✓ should have an add function
✓ should return 3 when passed 1, 2
✓ should return 5 when passed 3, 2
PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 3 of 3 SUCCESS (0.002 secs / 0.002 secs)
TOTAL: 3 SUCCESS
快速提示如何跳过某个测试,只需在其前面添加 x
xit ("should return 5 when passed 3, 2", function (){
expect(5).toEqual(add(3,2));
});
然后 Karma 会在控制台日志中报告此信息
Calculator
✓ should have an add function
✓ should return 3 when passed 1, 2
- should return 5 when passed 3, 2
表示最后一个测试已跳过。
完整源代码和测试代码清单
仅供参考,这是当我们为 `sub` 函数添加测试后 *index.spec.js* 文件的样子
describe ("Calculator", function (){
describe ("add function", function (){
it('should have an add function', function() {
expect(add).toBeDefined();
});
it ("should return 3 when passed 1, 2", function (){
expect(3).toEqual(add(1,2));
});
it ("should return 5 when passed 3, 2", function (){
expect(5).toEqual(add(3,2));
});
});
describe ("sub function", function (){
it('should have an sub function', function() {
expect(sub).toBeDefined();
});
it ("should return -1 when passed 1, 2", function (){
expect(-1).toEqual(sub(1,2));
});
it ("should return 1 when passed 3, 2", function (){
expect(1).toEqual(sub(3,2));
});
});
});
这是 *index.js* 文件的内容
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
此时运行 Karma 会在控制台输出以下内容
Calculator
add function
✓ should have an add function
✓ should return 3 when passed 1, 2
✓ should return 5 when passed 3, 2
sub function
✓ should have an sub function
✓ should return -1 when passed 1, 2
✓ should return 1 when passed 3, 2
如果你想查看完整的代码,可以在 Github 上 fork。
Wallaby
所有这些都非常酷,你可以打开你的终端,看着你的测试变成绿色。然而,就像现在所有的事情一样,有更好的工具。其中一个工具就是 Wallabyjs。让我向你展示它能做什么。
首先,您必须为您的编辑器安装 Wallaby。他们支持 Visual Studio Code、Atom、Sublime、Webstorm 等。
安装完成后,您需要设置其配置文件。让我们创建一个新文件并将其命名为 *wallaby.js*,然后将其放置在应用程序的根目录中。将以下代码复制/粘贴到其中
module.exports = function (wallaby) {
return {
files: [
'app/*.js'
],
tests: [
'tests/*.spec.js'
],
debug: true
};
};
*此时您可能需要重启编辑器*。此时,您只需从编辑器内部运行 Wallaby。在 Sublime 中,通过按 **CMD + SHIFT + P** 并选择 **Wallaby.js: Start** 来完成。Sublime 中还有一个方便的快捷键:**CMD +** . 后跟 **CMD + R**。
正如您将看到的,现在您可以在实际编辑器中获取有关测试通过(左侧绿色矩形)或失败的信息
实际上,Wallaby 还有更多功能,我将留给您探索。我与他们没有任何关联;我只是碰巧喜欢它。但是,为了避免您说我没有提及;就像每个伟大的工具一样,它也有其价格。如果您正在考虑(甚至抱怨)是否应该为某些软件付费,请阅读 Ambrose Little 关于您的生产力值多少钱?这篇精彩文章。
好了,这是 JavaScript 教程。现在我们来看看如何在 Ionic 框架应用程序中设置 Jasmine 和 Karma。
Ionic Framework 演示
为了学习本教程的这一部分,您需要在 npm 上全局安装 Ionic 和 Cordova 包。您可以在Ionic Framework:一份明确的 10,000 字指南中了解更多信息。
启动新项目并安装先决条件
首先,我们启动一个新的 Ionic 项目
ionic start ionic-tdd tabs
接下来,我们进入此文件夹并安装必要的先决条件。
cd ionic-tdd
npm install karma karma-jasmine karma-phantomjs-launcher jasmine-core --save-dev
设置 Karma
请确保您已从之前的 JavaScript 部分全局安装了 Karma。如果您没有,只需使用以下命令即可完成
npm install -g karma-cli
此外,此时,我们必须运行 `npm install` 来安装 Ionic *package.json* 文件中的所有先决条件。
最后,我们需要使用 Bower 安装 `angular-mocks`
bower install angular-mocks --save-dev
因为我们将用它来模拟某些 Angular 控制器。
完成此操作后,我们在项目的 *root* 目录中创建一个新文件夹。我们称之为 *tests*
mkdir tests
此外,让我们运行 `karma init` 命令(在项目的 *root* 目录中,在您的终端中运行此命令)。
您可以按照 JavaScript 部分中 Karma 的相同说明进行操作,只是不要输入源文件和测试文件的位置,我们将单独添加它们。
现在我们必须打开 *karma.conf.js* 文件并添加我们的源文件和测试文件
files: [
'www/lib/angular/angular.js',
'www/js/*.js',
'www/lib/angular-mocks/angular-mocks.js',
'tests/*.spec.js'
],
browsers: ['PhantomJS']
在下一步中,我们将配置我们的 *gulpfile.js* 文件,以便我们能够通过 Gulp 运行测试,因为 Ionic 使用它作为任务运行器。我们在文件顶部导入 Karma
var karmaServer = require('karma').Server;
我们编写了一个名为 `test` 的新任务
gulp.task('test', function(done) {
new karmaServer({
configFile: __dirname + '/karma.conf.js',
singleRun: false
}).start();
});
现在,我们可以像这样运行带 `test` 参数的 `gulp`:`gulp test`。
测试控制器
首先,我们在 *tests* 文件夹中创建一个新的 *tests/controllers.spec.js* 文件。
请注意,这现在不是 TDD 方法,因为我们已经在控制器中编写了代码。但是,如果您遇到一个没有单元测试的项目,这就是您将要做的事情。此外,所有为了使代码可测试而进行的重构,但那是另一个故事,留待以后再说……
我们从编写 `describe` 函数开始
describe('Controllers', function(){
});
接下来,由于这是 Angular,我们将有一个本地作用域变量(`var scope`)。在每个测试之前,我们必须加载 `starter.controller` 模块
beforeEach(module('starter.controllers'));
我们怎么知道需要设置这个模块?嗯,如果你看一下 *controllers.js* 文件,你会在顶部看到模块的名称是 `starter.controllers`。
此外,我们需要注入 Angular 的作用域变量并设置控制器。
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
$controller('AccountCtrl', {$scope: scope});
}));
为了将所有这些放在一个地方,您的 *controllers.spec.js* 文件应该如下所示
describe('Controllers', function(){
var scope;
beforeEach(module('starter.controllers'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
$controller('AccountCtrl', {$scope: scope});
}));
});
这是一段样板代码,您必须在每个测试中编写,所以尽管一开始可能看起来很奇怪,但在使用一段时间后,您就会习惯了。
再次,如果您想知道我们是如何找到 `AccountCtrl` 的,只需查看 *controllers.js* 文件以及我们正在尝试测试的控制器的名称。
最后,我们来测试。假设我们想测试 `settings` 对象上的 `enableFriends` 属性是否设置为 `true`,我们将编写如下测试
it('should have enableFriends property set to true', function(){
expect(scope.settings.enableFriends).toEqual(true);
});
现在我们用 `gulp test` 运行我们的测试,我们可以看到我们的测试通过了。
测试服务/工厂
现在我们将为我们的工厂 `Chats` 编写测试。如您所见,该工厂有三个函数,用于获取所有聊天(当前是硬编码的)、删除聊天和获取特定聊天。
首先,我们将在 *tests* 文件夹中创建一个名为 *services.spec.js* 的新文件,并添加我们的 `describe` 函数
describe('Chats Unit Tests', function(){
});
接下来,我们将设置 `module` 并注入 `Chats` 工厂
var Chats;
beforeEach(module('starter.services'));
beforeEach(inject(function (_Chats_) {
Chats = _Chats_;
}));
现在,我们可以编写我们的第一个测试,嗯,让我们首先测试我们的 `Chats` 工厂是否已定义
var Chats;
beforeEach(module('starter.services'));
beforeEach(inject(function (_Chats_) {
Chats = _Chats_;
}));
然后,我们可以检查它是否返回五个 `chats`
it('has 5 chats', inject(function(Chats) {
expect(Chats.all().length).toEqual(5);
}));
如果此时,我们还想看到更漂亮的规格报告,我们应该终止当前运行的 gulp 进程。安装所需的包
npm install karma-spec-reporter --save-dev
npm install jasmine-spec-reporter --save-dev
调整 *karma.conf.js* 文件
reporters: ['spec'],
并使用 `gulp test` 重新运行 gulp。
总而言之,您的 *services.spec.js* 文件应该如下所示
describe('Chats Unit Tests', function(){
var Chats;
beforeEach(module('starter.services'));
beforeEach(inject(function (_Chats_) {
Chats = _Chats_;
}));
it('can get an instance of my factory', inject(function(Chats) {
expect(Chats).toBeDefined();
}));
it('has 5 chats', inject(function(Chats) {
expect(Chats.all().length).toEqual(5);
}));
});
如果你想查看完整的代码,可以在 Github 上 fork。
Wallaby
如果你想在 Ionic 中尝试 Wallaby,你只需创建 *wallaby.js* 文件并设置配置
module.exports = function (wallaby) {
return {
files: [
'www/lib/angular/angular.js',
'www/js/*.js',
'www/lib/angular-mocks/angular-mocks.js',
],
tests: [
'tests/*.spec.js'
],
debug: true
};
};
结论
到目前为止,我个人的心得是,即使您不完全接受 TDD 的理念,我也强烈建议您至少开始使用单元测试,因为您已经看到了它们的价值。至于整个 TDD 理念,我还在观望这一切会如何发展,因为我觉得要正确地采纳它需要一定的纪律,直到它被正确地实施。
当然,所有这些都只是冰山一角。我只是简单介绍了单元测试以及 Jasmine 作为测试环境能做什么。我希望在不久的将来,我能够与您分享一些最佳实践和一些高级技术。在此之前,我希望这能对你们中的一些人有所帮助,至少能让你们开始。
演示项目可在 Github 上查找
是的,选择红药丸。😉
如果有人感兴趣,下面是我在阅读材料和沿途收集的笔记方面,通往稍微有点棒的 TDD 的路径。
树屋课程
- 谨慎使用端到端测试(这与 Google 文章一致)
- 套件和规格
摩卡 --报告喵
"scripts": {"test":mocha, "test:watch":"mocha --watch ./test ./"}
npm run test:watch
关于这个主题的书籍
博客文章
JS TDD 简介
TDD 的优势
- 它迫使人思考
- 它使调试更容易
- 它让编码更有趣
TDD 是一种编写软件的方法,您**在编写应用程序代码之前先编写测试**。基本步骤是
- **红色**——编写一个测试并确保它失败
- **绿色**——编写最简单、最容易的代码以使测试通过
- **重构**——优化和/或简化应用程序代码,确保所有测试仍然通过
你必须先思考,然后编写测试。
// flickr-fetcher-spec.js
'use strict';
var expect = require('chai').expect;
describe('FlickrFetcher', function() {
it('should exist', function() {
var FlickrFetcher = require('./flickr-fetcher.js');
expect(FlickrFetcher).to.not.be.undefined;
});
});
我们正在使用行为驱动开发(BDD)风格来编写测试。这是 TDD 的一种变体,其中测试以以下形式编写
- 描述 `[事物]`
- 它应该 `[做某事]`
`[thing]` 可以是模块、类、方法或函数。Mocha 包含 `describe()` 和 `it()` 等内置函数,使得以这种风格编写成为可能。
在没有失败测试之前,不要编写模块代码。那么我该怎么办?我再写一个测试。
经验法则是,比较数字、字符串或布尔值时使用 `**equal**`,比较数组或对象时使用 `**eql**`。**注意**:在其他一些测试框架中,`eql` 被命名为 `deepEqual`。但是,请注意 Jasmine 只有 `toEqual`。
JS TDD 第二部分简介
我用来替换 `$.getJSON()` 的 `fakeFetcher()` 函数被称为 **stub**。stub 是一段与“真实”代码具有相同 API 和行为,但功能大大减少的代码。通常,这意味着**返回静态数据**而不是与某些外部资源交互。
典型的存根可能会替换以下内容
- 对关系数据库的查询
- 与文件系统的交互
- 接受用户输入
- 耗时较长的复杂计算
TDD 应该很有趣
- 功能测试(E2E)
- 集成测试,比 E2E 更常见
关于 JavaScript 测试主题,大名鼎鼎的 Eric Elliot
- 单元测试、集成测试和功能测试都是自动化测试的类型,它们构成了持续交付(一种开发方法,允许您在数天或数小时而非数月或数年内安全地将更改发布到生产环境)的重要基石。
- 一个进入生产环境的 Bug 的成本比自动化测试套件捕获的 Bug 的成本高出许多倍。换句话说,TDD 具有压倒性的正向投资回报率。
- 您无需在单元测试、功能测试和集成测试之间进行选择。全部使用它们,并确保您可以将每种类型的测试套件与其他测试套件隔离运行。
- 单元测试
- 确保应用程序的各个组件按预期工作。断言测试**组件 API**。
- 集成测试
- 确保组件协作按预期工作。断言可以测试组件 API、UI 或副作用(例如数据库 I/O、日志记录等)。
- 功能测试
- 确保应用程序从用户的角度按预期工作。断言主要测试**用户界面**。
例如,您的应用程序可能需要将 URL 路由到路由处理程序。可以针对 URL 解析器编写单元测试,以确保 URL 的相关组件被正确解析。另一个单元测试可以确保路由器为给定的 URL 调用正确的处理程序。
然而,如果你想测试当一个特定的 URL 被发送时,相应的记录是否被添加到数据库中,那将是一个集成测试,而不是单元测试。
是的,你编写了更多的代码,但研究表明,**使用 TDD 进行良好的测试覆盖可以使 Bug 密度降低 40% – 80%**。
他的另外两篇文章
- TDD 太耗时了。业务团队永远不会批准。
- 你无法在知道设计之前编写测试,也无法在实现代码之前知道设计。
- 在开始编写代码之前,你必须编写所有测试
- 红、绿,总是重构?
- 一切都需要单元测试
这里有一个能改变你人生的建议:模拟是一种代码异味。
- 一份好的测试失败错误报告包含什么?
- 你测试了什么?
- 它应该做什么?
- 输出是什么(实际行为)?
- 预期的输出是什么(预期行为)?
几篇不错的通用博文
- Google 对 E2E、集成和单元测试的看法
- TDD 已死,测试万岁
- 测试驱动开发并非测试
- TDD 中的三角测量
- JavaScript 中测试驱动开发简介
- 让你的函数纯粹
- 编写优秀的单元测试
- 单元测试并非为了发现 bug,但在重构时非常出色
- 为乐趣和利润测试 Angular 中的服务
- 如果有一种方法可以减少您编写(或管理)的代码中的缺陷数量,提高交付物的质量和上市时间,并使后来者的维护更容易——您会这样做吗?
- 你听过多少次“编写测试不如交付完成的代码重要”的变体?如果你像我一样,那真是太多了,如果你在没有任何测试的情况下工作,那上帝保佑你。程序员是人,我们都会犯错。所以测试你的代码吧。测试我的代码帮助我捕捉到未预见的问题,避免它们变成彻底的 bug,防止未来的回归,或者仅仅是更好地架构代码的次数非常惊人。而这还是一个曾经讨厌为代码编写测试的人说的话。我痛恨它。
- Jasmine 是一个行为驱动开发框架,这是一种迂回的说法,意思是我们的测试包含对其正在测试的模块以及它们应该做什么的描述。
- 您可以在 JavaScript 中非常轻松地创建存根对象,因此如果不需要引入间谍(spy)的额外复杂性,那就这样做。
- 永远把代码写得好像维护你代码的人是一个暴力的精神病患者,并且知道你住在哪里一样。
- 一个奇怪的技巧将永远改变你的编码方式:Javascript TDD
- 你是否曾修复一个 Bug,却发现它在系统的另一个部分造成了严重的破坏?而且直到客户恐慌地打电话给支持人员时你才发现?
- 你是否曾害怕触碰一段复杂的代码,担心可能会破坏它,然后永远无法修复?……即使那是你写的代码?
- 你是否曾发现一段代码,你很确定它不再被使用了,应该被删除?但你还是把它留在那儿以防万一?
- **TDD 不是关于测试**。它是一种思考和编码的方式,恰好涉及测试。
- TDD 与单元测试并非一回事。单元测试是一种测试类型。TDD 是一种**编码技术**。
- 红色——编写一个不起作用的微小测试,也许一开始甚至无法编译
- 绿色——快速使测试通过,在此过程中犯下任何必要的错误
- 重构——消除为了使测试通过而创建的所有重复代码
最后,Ionic (Angular) 相关的 TDD 文章
如何为您的 Ionic 应用编写自动化测试
- 在单元测试的例子中,我们看到我们需要模拟依赖项。对于集成测试,根据您要一起测试的单元,您仍然可以模拟某些依赖项,或者根本不模拟。
Ionic 中的 TDD
- 简短教程,展示如何使用 Jasmine 运行 Karma
单元测试你的 Ionic Framework 应用
This tutorial was actually great (which I can't say for the previous two) and I've learned the most out of it and finally set up a test environment. Fun fact: I added `npm install --save-dev karma-nyan-reporter` and now am running my tests like this: `karma start tests/my.conf.js --reporters nyan
其他一些 AngularJS TDD 博客文章
- 使用 Codeship 持续集成、Jasmine 和 Karma 对 AngularJS Ionic 应用进行单元测试
- AngularJS 中的单元测试最佳实践
- 官方 AngularJS 单元测试指南
- **下划线表示法**:下划线表示法(例如:`_$rootScope_`)是 AngularJS 社区中广泛使用的约定,用于在测试中保持变量名清晰。这就是为什么 `$injector` 在匹配参数时会去除开头和结尾的下划线。下划线规则仅在名称以一个下划线开头和结尾时适用,否则不会发生替换。
- 将 Karma 和 Jasmine 添加到现有 Ionic 项目
- 单元测试 AngularJS 应用程序
- 使用 Jasmine 和 Karma 测试 AngularJS
我的笔记
- 为了让 Karma 运行 PhantomJS,需要 `npm install phantomjs-prebuilt`。
-
不得不更改代码中实际的 Angular mocks 1.5.1 错误(https://github.com/angular/angular.js/issues/14251)。
此时,测试终于通过了!
工具
Wallabyjs – 一个很棒的工具
Ionic 框架中的 #TDD 简介 https://#/RUyy0rc0h8 @Ionicframework pic.twitter.com/tw22XMmhzn