开发指南-测试 pipe还剩半章

This commit is contained in:
Zhicheng Wang 2016-05-30 16:39:21 +08:00
parent b489d0a0a8
commit 1f1b3e8496
6 changed files with 343 additions and 133 deletions

View File

@ -3,22 +3,22 @@
"index": {
"title": "测试概览",
"intro": "Angular 2应用测试的技术与实践"
"intro": "测试Angular 2应用的技术与实践"
},
"jasmine-testing-101": {
"title": "Jasmine测试101",
"intro": "用Jasmine测试任何东西的基础知识"
"intro": "用Jasmine测试任何东西时所需的基础知识"
},
"application-under-test": {
"title": "准备测试的应用",
"title": "测试的应用",
"intro": "我们将要测试的应用概览"
},
"first-app-tests": {
"title": "第一个应用测试",
"intro": "首先测试应用中一个简单的与Angular无关的部分"
"title": "应用的第一个测试",
"intro": "首先测试应用中一个简单的与Angular无关的部分"
},
"testing-an-angular-pipe": {

View File

@ -3,16 +3,16 @@ include ../_util-fns
:marked
Well need an Angular application to test, one as simple as possible while having most of the angular features we want to test.
我们将需要一个Angular应用程序来测试一个尽量简单但几乎拥有所有我们想要测试angular特性的应用程序。
我们首先得有一个待测试的Angular应用程序一个尽可能简单但几乎拥有所有待测Angular特性的应用程序。
What better app than our own [The Tour of Heroes](../tutorial/toh-pt5.html)? We're already quite familiar with it and it fits our criteria, so let's try to test what we've done there.
什么应用程序比我们自己的[英雄指南](../tutorial/toh-pt5.html)更合适?我们已经很熟悉它了,它也适合我们的需求,所以让我们来测试一下我们已经完成了的功能
还有什么会比我们自己做的[英雄指南](../tutorial/toh-pt5.html)更合适呢?我们已经很熟悉它了,它也挺适合我们的课程目标,那就来测试一下我们已经完成的那些功能吧
We might end up modifying it a bit, because it doesn't have everything we want to test, but it's the perfect starting point.
我们可能需要对它做一些修改,因为它没有所有我们想要测试的需求,但是这是一个完美的起点。
我们可能需要对它做一些修改,因为它还没有覆盖我们要测试的所有东西,但是它是一个完美的起点。
Create a copy of the Tour of Heroes app so that we can fiddle without fear.
复制一个英雄指南应用程序,这样我们可以肆无忌惮的摆弄它
先复制一份《英雄指南》的源码,这样我们就可以肆无忌惮的摆弄它了

View File

@ -3,22 +3,31 @@ include ../_util-fns
:marked
In this chapter we'll setup the environment for testing our sample application and write a few easy Jasmine tests of the app's simplest parts.
在这个章节,我们将为我们的样本应用程序设置测试环境,针对应用程序的最简单的部分,编写几个简单的Jasmine测试。
本章中,我们将为范例应用设置测试环境,并针对应用程序中最简单的部分,写几个容易点儿的Jasmine测试。
We'll learn:
我们会学到:
- to test one of our application files
- 测试我们应用程序的一个文件
- 测试我们应用程序中的一个文件
- why we prefer our test files to be next to their corresponding source files
- 为什么我们比较喜欢把我们的测试文件放到它对应的源码旁边
- 为什么我们更喜欢把测试文件放在它对应的源码旁边
- to run tests with an `npm` command
- 使用`npm`指令运行测试
- 用`npm`命令运行测试
- load the test file with SystemJS
- 使用SystemJs加载测试文件
.callout.is-helpful
header Prior Knowledge 预先需要的知识
header Prior Knowledge
header 预备知识
:marked
The Unit Testing chapters build upon each other. We recommend reading them in order.
We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
@ -26,54 +35,55 @@ include ../_util-fns
the [Tour of Heroes](../tutorial/) tutorial
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
本单元测试章节是在其它章节的基础上编写的。我们建议你按顺序阅读它们。我们同时假设你已经对Angular 2的原理、在[“快速起步”](../quickstart.html)里介绍的工具、
[英雄指南](../tutorial)和<code>npm</code>, <code>gulp</code>, and <code>live-server</code>等工具都所有了解。
这一章单元测试是在其它章节的基础上写的。我们建议你按顺序阅读它们。同时我们假设你已经对Angular 2的原理、[“快速起步”](../quickstart.html)中介绍的工具、
[英雄指南](../tutorial)、<code>npm</code>、<code>gulp</code>和<code>live-server</code>等工具都已经有所了解。
.l-main-section
:marked
## Create the test-runner HTML
## 新建一个测试运行器(test-runner) HTML
## 创建测试运行器(test-runner)HTML
Locate the folder that contains the application `index.html` for your testing copy of Tour of Heroes.
在英雄指南的测试拷贝版里找到含`index.html`的目录。
英雄指南的测试拷贝版里找到含`index.html`的目录。
Create a new, sibling HTML file, ** `unit-tests.html` ** and copy over the same basic material from the `unit-tests.html` in the [Jasmine 101](./jasmine-testing-101.html) chapter.
在隔壁建立一个新的HTML文件**`unit-tests.html`**,从[Jasmine 101](./jasmine-testing-101.html)章节,将`unit-tests.html`里面的基础内容拷贝到此文件中
新建一个同级的HTML文件**`unit-tests.html`**,从[Jasmine 101](./jasmine-testing-101.html)一章中,将`unit-tests.html`里面的基本素材拷贝进该文件
+makeExample('testing/ts/unit-tests-2.html', 'test-runner-base', 'unit-tests.html')
:marked
We're picking up right where we left off. All we've done is change the title.
我们将从上次的的基础上开始。我们只修改了标题。
我们把上次的重点作为起点。那时我们只修改完了标题。
.l-main-section
:marked
## Update `package.json` for testing
## 为测试更新`package.json`
## 为测试更新`package.json`
We must install the Jasmine package as well:
我们必须安装Jasmine包
我们必须安装Jasmine包
pre.prettyprint.lang-bash
code npm install jasmine-core --save-dev --save-exact
.alert.is-important Be sure to install <code>jasmine-core</code> , not <code>jasmine</code>!
.alert.is-important 请注意,安装<code>jasmine-core</code>,而不是<code>jasmine</code>!
.alert.is-important 请确保安装的是<code>jasmine-core</code>,而不是<code>jasmine</code>
:marked
Let's make one more change to the `package.json` script commands.
让我们在`package.json`的脚本命令部分再修改一项
让我们在`package.json`的脚本命令区再做一项修改
**Open the `package.json` ** and scroll to the `scripts` node and add in a new one:
**打开`package.json`**,滚动到`scripts`节点,添加下面这行:
**打开`package.json`**,滚动到`scripts`节点,添加下面这行:
code-example(format="").
"test": "live-server --open=unit-tests.html"
@ -81,17 +91,17 @@ code-example(format="").
:marked
That command will launch `live-server` and open a browser to the `unit-tests.html` page we just wrote.
该指令将启动`live-server`,并使用浏览器,打开我们刚新建的`unit-tests.html`页面。
该指令将启动`live-server`,并在浏览器中打开我们刚写的`unit-tests.html`页面。
.l-main-section
:marked
## First app tests
## 第一个应用程序测试
We can start testing *some* of our app right away. For example, we can test the `Hero` interface:
我们可以立刻开始测试我们应用程序的*一些*代码。比如,我们可以测试`Hero`接口:
我们现在可以开始测试应用程序中的*某些*代码了。比如,我们可以测试`Hero`接口:
+makeExample('testing/ts/app/hero.ts')
@ -109,80 +119,136 @@ code-example(format="").
Notice that we surrounded our tests with ** `describe('Hero')` **.
请注意,我们把我们的测试围在**`describe('Hero')`**里面
请注意,我们把这些测试包裹在**`describe('Hero')`**中了
**By convention, our test always begin with a `describe` that identifies the application part under test.**
**按惯例,我们的测试都已`describe`开始,它是用来标识该测试是用来测试哪个应用程序部分的。
**按惯例,我们的测试总会以`describe`开始,它标识出所测的是应用程序的哪个部分。**
The description should be sufficient to identify the tested application part and its source file.
Almost any convention will do as long as you and your team follow it consistently and are never confused.
这个说明应该能足够标识被测试应用程序部分和相关源代码。几乎任何
这个说明要足以标识出所测的部件和相关源码。无论采用什么约定,都应该让你和你的团队始终如一的遵循它,并且不会导致混淆。
But we haven't saved this test yet.
不过,我们现在还没保存这个测试呢。
.l-main-section
:marked
## Where do tests go?
## 测试文件放在哪里?
Some people like to keep their tests in a `tests` folder parallel to the application source folder.
有人喜欢把他们的测试保存在与应用程序源码平级的`tests`目录下。
We are not those people. We like our unit tests to be close to the source code that they test. We prefer this approach because
但我们不喜欢。我们更喜欢让这些单元测试放得离它们要测的源码尽可能近一点。我们更喜欢这种方式是因为:
- The tests are easy to find
- 更容易找到测试
- We see at a glance if an application part lacks tests.
- 我们只要扫一眼就能知道应用程序中哪部分缺少测试
- Nearby tests can teach us about how the part works; they express the developers intention and reveal how the developer thinks the part should behave under a variety of circumstances.
- 附近的测试能告诉我们这部分是如何工作的,它们能表达开发者的意图,并揭示出开发者认为在各种情况下这部分该有怎样的行为。
- When we move the source (inevitable), we remember to move the test.
- 当我们移动源码时(难免的),总能记得同时移动测试。
- When we rename the source file (inevitable), we remember to rename the test file.
- 当我们重命名源码文件时(难免的),总能记得同时改名测试文件。
We can't think of a downside. The server doesn't care where they are. They are easy to find and distinguish from application files when named conventionally.
我们想不到有什么负面效果。服务器不在乎它们放在哪里。如果按照约定命名它们,它们也很容易从应用本身的文件中找到和区分出来。
.l-sub-section
:marked
You may put your tests elsewhere if you wish.
We're putting ours inside the app, next to the source files that they test.
如果喜欢,你也可以自己的测试放在其它地方。
反正我们是把它们放进了我们的应用代码中,紧跟着它们所要测试的源码文件。
.l-main-section
:marked
## First spec file
## 第一个规约文件
**Create** a new file, ** `hero.spec.ts` ** in `app` next to `hero.ts`.
在`app`下,紧挨着`hero.ts`**创建**一个名叫`hero.spec.ts`的新文件。
Notice the ".spec" suffix in the test file's filename, appended to the name of the file holding the application part we're testing.
注意,测试文件名称中的“.spec”后缀被追加到了被测试的应用程序部件的基本名后面。
.alert.is-important All of our unit test files follow this .spec naming pattern.
.alert.is-important 我们所有的单元测试文件都遵循了这种.spec命名模式。
:marked
Save the tests we just made in `hero.spec.ts`:
保存我们刚刚在`hero.spec.ts`中写的这些测试:
+makeExample('testing/ts/app/hero.spec.ts', 'base-hero-spec')
:marked
### Import the part we're testing
### 导入我们准备测试的部件
We have an `import {Hero} from './hero' ` statement.
这里我们有个`import {Hero} from './hero' `语句。
If we forgot this import, a TypeScript-aware editor would warn us, with a squiggly red underline, that it can't find the definition of the `Hero` interface.
如果我们忘记了这条导入语句支持TypeScript的编辑器就会通过一个红色曲线警告我们它找不到`Hero`接口的定义。
### Update unit-tests.html
### 更新unit-tests.html
Next we update the `unit-tests.html` with a reference to our new `hero.spec.ts` file. Delete the inline test code. The revised pertinent HTML looks like this:
接下来我们在`unit-tests.html`中用一个到这个新的`hero.spec.ts`文件的引用代替原来的内联测试代码。
修改过的HTML看起来是这样的
+makeExample('testing/ts/unit-tests-2.html', 'load-hero-and-spec')(format=".")
:marked
### Run and Fail
### 运行,并失败
Look over at the browser (live-server will have reloaded it). The browser displays
看看浏览器(live-server应该已经刷新了它)。其中显示的是:
figure.image-display
img(src='/resources/images/devguide/first-app-tests/Jasmine-not-running-tests.png' style="width:400px;" alt="Jasmine not running any tests")
:marked
That's Jasmine saying "**things are _so_ bad that _I'm not running any tests_.**"
这是Jasmine在说“**事情_很糟_因为_我没找到任何测试_。**”
Open the browser's Developer Tools (F12, Ctrl-Shift-i). There's an error:
打开浏览器开发工具(F12或Ctrl-Shift-I)就会看到一个错误:
code-example(format="" language="html").
Uncaught ReferenceError: System is not defined
@ -190,31 +256,60 @@ code-example(format="" language="html").
:marked
## Load tests with SystemJS
## 使用SystemJS加载这些测试
The immediate cause of the error is the `export` statement in `hero.ts`.
That error was there all along.
It wasn't a problem until we tried to `import` the `Hero` interface in our tests.
导致这个错误的直接原因,就是`hero.ts`中的`export`语句。
那个错误一直都存在。
除非我们尝试把`Hero`接口`import`到测试中,否则这个问题会一直存在。
Our test environment lacks support for module loading.
Apparently we can't simply load our application and test scripts like we do with 3rd party JavaScript libraries.
我们的测试环境中缺少了对模块加载的支持。
显然我们没法像使用第三方JavaScript库那样简单的加载起应用代码和测试脚本。
We are committed to module loading in our application.
Our app will call `import`. Our tests must do so too.
我们得在本应用中引入模块加载机制。
我们在应用代码中要调用`import`,测试代码中也要如此。
We add module loading support in four steps:
我们要通过四步儿来添加对模块加载的支持。
1. add the *SystemJS* module management library
1. 添加*SystemJS*模块管理库
1. configure *SystemJS* to look for JavaScript files by default
1. 配置*SystemJS*来默认查找JavaScript文件
1. import our test files
1. 导入我们的测试文件
1. tell Jasmine to run the imported tests
1. 让Jasmine运行已导入的测试
These steps are all clearly visible, in exactly that order, in the following lines that
replace the `<body>` contents in `unit-tests.html`:
在`unit-tests.html`中用于替换`<body>`内容的那些代码行中,这些步骤清晰可见:
+makeExample('testing/ts/unit-tests-3.html', 'systemjs')(format=".")
:marked
Look in the browser window. Our tests pass once again.
到浏览器窗口中看看,我们的测试又一次通过了。
figure.image-display
img(src='/resources/images/devguide/first-app-tests/test-passed-once-again.png' style="width:400px;" alt="Tests passed once again")
@ -223,38 +318,75 @@ figure.image-display
:marked
## Observations
## 观测结果
### System.config
System.js demands that we specify a default extension for the filenames that correspond to whatever it is asked to import.
Without that default, it would translate an import statement such as `import {Hero} from './hero'` to a request for the file named `hero`.
Not `hero.js`. Just plain `hero`. Our server error with "404 - not found" because it doesn't have a file of that name.
System.js强烈要求我们为想import的那些文件指定一个默认扩展名。
如果没有默认扩展名它就会把import语句翻译成`import {Hero} from './hero'`来请求一个名叫`hero`的文件,而不是`hero.js`。服务器就会返回一个“404 - not found”
因为它找不到那个不带扩展名的文件。
Once configured with a default extension of 'js',&nbsp; SystemJS requests `hero.js` which *does* exist and is promptly returned by our server.
一旦把默认扩展名配置为'js'SystemJS就会请求`hero.js`,它存在,并且立即被服务器成功返回。
### Asynchronous System.import
### 异步System.import
The call to `System.import` shouldn't surprise us but it's asynchronous nature might.
If we ponder this for a moment, we realize that it must be asynchronous because
System.js may have to fetch the corresponding JavaScript file from the server.
Accordingly, `System.import` returns a promise and we must wait for that promise to resolve.
Only then can Jasmine start evaluating the imported tests.
对`System.import`的调用不会让我们感到意外 —— 除了它的异步性。
如果我们仔细考虑一会儿就会认识到它必须是异步的因为SystemJS不得不从服务器上取得相应的JavaScript文件。
因此,`System.import`会返回一个承诺(Promise),我们得等待这个承诺被解决。
然后Jasmine才能开始执行这些导入的测试。
### window.onload
Jasmine doesn't have a `start` method. It wires its own start to the browser window's `load` event.
That makes sense if we're loading our tests with script tags.
The browser raises the `load` event when it finishes loading all scripts.
Jasmine没有一个`start`方法。它把自己的启动代码挂接到了浏览器窗口的`load`事件上。
如果我们通过`script`标签来启动测试,这样做自然是有意义的。
当浏览器加载完所有脚本时,它就会触发`load`事件。
But we're not loading test scripts inline anymore.
We're using the SystemJS module loader and it won't be done until long after the browser raised the `load` event.
Meanwhile, Jasmine started and ran to completion … with no tests to evaluate … before the import completed.
但我们不再用内联的方式加载测试脚本了。
我们在使用SystemJS模块加载器除非浏览器触发了`load`事件,否则它什么也不会做。
同时在这些import完成之前Jasmine自己已经启动并且运行结束了不过一个测试也没有执行。
So we must wait until the import completes and only then call the window `onLoad` handler.
Jasmine re-starts, this time with our imported test queued up.
所以我们必须等待import执行完然后才能调用window的`onLoad`处理器。
Jasmine重新启动但这次它带着我们导入的这个测试队列。
.l-main-section
:marked
## What's Next?
## 下一步呢?
We are able to test a part of our application with simple Jasmine tests.
The part was a stand-alone interface that made no mention or use of Angular.
我们已经能用简单的Jasmine测试来测试应用的一部分。
这部分是一个独立的接口它没有引用或使用任何来自Angular的东西。
That's not rare but it's not typical either.
Most of our application parts make some use of the Angular framework.
Let's test a *pipe* class that does rely on Angular.
这虽然不算罕见,但也不够典型。
我们应用程序中的大部分都会使用到Angular框架的某些东西。
我们再来测试一个*管道*类它依赖于Angular。

View File

@ -54,7 +54,7 @@
1. The Application Under Test
1. 测试的应用程序
1. 测试的应用程序
1. Test a class

View File

@ -4,38 +4,47 @@ include ../_util-fns
Well write our tests with the [Jasmine test framework](http://jasmine.github.io/2.3/introduction.html).
Well start by getting *some* tests to work - *any* tests at all.
我们将使用[Jasmine测试框架](http://jasmine.github.io/2.3/introduction.html)编写我们的测试。
我们以把*一些*测试弄成功为开始
我们将使用[Jasmine测试框架](http://jasmine.github.io/2.3/introduction.html)编写测试。
先从让*某些*测试跑起来开始 —— *任何一个*测试都可以
We will learn
我们会学到:
- basic Jasmine testing skills
- 基础Jasmine测试技能
- Jasmine测试的基础技能
- to run our tests in the browser
- 在浏览器里面运行我们的测试
- 在浏览器里运行我们的测试
- to write simple Jasmine tests in TypeScript
- 使用TypeScript编写简单的Jasmine测试
- 用TypeScript编写简单的Jasmine测试
- to debug a test in the browser
- 在浏览器里调试一个测试
**Create a new project folder** perhaps called `angular2-unit-testing`.
**新建一个项目目录**名为`angular2-unit-testing`。
**新建一个项目目录**名为`angular2-unit-testing`。
.l-main-section
:marked
## Install npm packages locally
## 本地安装npm包
Next follow all of the steps prescribed in “Install npm packages locally” of the
[QuickStart](../quickstart.html).
下一步,完成[“快速起步”](../quickstart.html)中的“本地安装npm包”的每一步。
接下来,完成[“快速起步”](../quickstart.html)一章中“本地安装npm包”的每一步。
Well also add the Jasmine package via `npm`:
然后我们通过npm添加Jasmine包
然后我们通过`npm`添加Jasmine包
pre.prettyprint.lang-bash
code npm install jasmine-core --save-dev --save-exact
@ -44,46 +53,47 @@ pre.prettyprint.lang-bash
:marked
Be sure to install `jasmine-core` , not `jasmine`!
确认安装的是`jasmine-core`不是`jasmine`
请确保安装的是`jasmine-core`,而不是`jasmine`
:marked
**Create a sub-folder `src` ** for our tests and then **cd into it**.
我们的测试**新建一个子目录`src`**,然后**cd到里面**。
这次测试**新建一个子目录`src`**,然后**cd进去**。
We are going to **display and control our tests in the browser**.
我们将会**在浏览器里面显示和控制我们的测试**。
我们将会**在浏览器里显示和控制这些测试**。
.l-sub-section
:marked
The browser is nice during development of a few tests. Its not the best venue for working with a lot of tests and it wont do at all for build automation.
Well switch to the karma test-runner when the time comes. But the browser will do for now.
在我们开发一些测试的过程中,浏览器很有帮助。它却并不是最好的开发很多测试的地方,它更不能用来做建构自动化
到时我们将会切换到Karma测试运行器。但是现在先用浏览器
在我们开发某些测试的过程中,浏览器很有帮助。如果要写很多测试,它并不是最佳选择,更不用说用到自动化构建中了
等那时候我们就会切换到Karma测试运行器(test-runner)。但目前还是先用浏览器吧
:marked
Create a new file called`unit-tests.html` and enter the following:
新建一个文件叫做`unit-tests.html`,并把下面的内容输入进去:
新建一个名叫`unit-tests.html`的文件,并输入如下内容:
+makeExample('testing/ts/unit-tests-0.html', 'no-script', 'unit-tests.html')
:marked
In the head we have three Jasmine scripts and one Jasmine css file. Thats the foundation for running any tests.
head里面我们有三个Jasmine脚本和一个Jasmine css文件。它们是运行任何测试的奠基石
`head`标记中有三个Jasmine脚本和一个Jasmine css文件。它们是运行任何测试的基础
Well write our first test with inline JavaScript inside the body tag:
我们接下来以内联JavaScript的形式在body表示里面编写我们的第一个测试:
然后在`body`标记中,我们以内联JavaScript的形式编写第一个测试
+makeExample('testing/ts/unit-tests-0.html', 'body')(format='.')
:marked
Now open `unit-tests.html` in a browser and see the Jasmine HTML test output:
现在,在浏览器里打开`unit-tests.html`看到Jasmine HTML的测试结果:
现在,在浏览器中打开`unit-tests.html`就能看到HTML格式的Jasmine测试结果:
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/jasmine-1-spec-0-failures.png' style="height:170px;" alt="Jasmine HTML test output")
@ -91,69 +101,74 @@ figure.image-display
:marked
It doesnt get much simpler than that!
个已经是简单的不能再简单了。
已经简单得不能再简单了。
.l-main-section
:marked
## First TypeScript Test
## 第一个TypeScript测试
Perhaps too simple. We wont write our entire test suite inside one HTML file.
Lets **extract** that line of test code to a **new file in `src` called `1st.spec.ts` ** .
或许过于简单。我们不会再一个HTML文件里面编写整套测试。让我们把这行测试代码**提取**到**`src`目录下的新文件`1st.spec.ts`**。
也许简单得过头儿了。我们不会在HTML文件中编写测试套件。我们来把这些测试代码**提取**到**`src`目录下的新文件`1st.spec.ts`**。
.l-sub-section
:marked
Among Jasmine developers, a test is known as a “spec” and test filenames include the word “spec”. Well stick with that convention.
在Jasmine开发者中一个测试通常被叫做“spec”而且测试文件名包含单词“spec”。我们将遵循这个规则。
在Jasmine开发者中测试通常被叫做“规约(spec)”,而且测试文件字也会包含单词“spec”。我们将遵循这个规则。
:marked
The test we wrote is valid TypeScript because any JavaScript is valid TypeScript. But lets make it more modern with an arrow function:
我们编写的测试是一个有效的TypeScript因为所有的JavaScript都是有效的TypeScript。但是让我们使用箭头函数让它更加现代
我们写的这个测试也是一个有效的TypeScript因为所有JavaScript都是有效的TypeScript。但TypeScript允许我们使用箭头函数来让它显得更有现代感
+makeExample('testing/ts/1st.spec.ts', 'it', '1st.spec.ts')
:marked
Now modify `unit-tests.html` to load the script:
现在修改`unit-tests.html`来加载上面的脚本:
现在修改`unit-tests.html`以加载此脚本:
+makeExample('testing/ts/unit-tests-1.html', 'script')
:marked
Hold on! We wrote a TypeScript file but were loading a JavaScript file?
等等!我们编写了TypeScript文件但是我们却加载了一个JavaScript文件
等等!我们刚写的明明是TypeScript文件为何这里要加载JavaScript文件
Thats a reminder that we need to compile our TypeScript test files as we do our TypeScript application files. Do that next.
这是一个提醒我们需要编译我们的TypeScript测试文件就像我们编译我们的TypeScript应用程序文件一样。我们接下来做这个。
这是提醒我们要编译TypeScript测试文件就像编译应用程序中的TypeScript文件那样。接下来我们就做这个。
.l-main-section
:marked
## Prepare for TypeScript
## 为TypeScript做准备
As weve seen before, we first have to tell the compiler how to compile our TypeScript files with
a ** `tsconfig.json` **.
我们见过的一样,我们必须通过*`tsconfig.json`*,告诉编译器怎么编译我们的TypeScript文件。
以前见过的一样,我们必须用**`tsconfig.json`**文件告诉编译器如何编译我们的TypeScript文件。
We can copy one from the quickstart we wrote previously and paste it into our src sub-folder.
It should look something like this:
我们可以从“快速起步”拷贝一个之前已写好的粘贴到我们的src子目录它应该像这样:
我们可以从“快速起步”中拷贝一个写好的粘贴到src子目录下像这样:
+makeExample('testing/ts/tsconfig.1.json', null, 'tsconfig.json')
:marked
## Compile and Run
## 编译和运行
Compile in the terminal window using the npm script command
在终端窗口使用npm脚本命令编译
在终端窗口使用npm脚本命令进行编译:
pre.prettyprint.lang-bash
code npm run tsc
@ -164,20 +179,21 @@ pre.prettyprint.lang-bash
what `it` and `expect` are because they lack the typing files that describe Jasmine.
We can ignore those annoying complaints for now as they are harmless.
我们的编辑器和编译器可能会抱怨,说它们不知道什么是`it`和`expect`因为它们缺少描述Jasmine的typing文件。我们暂时可以忽略这些讨厌的抱怨它们目前并无害处。
我们的编辑器和编译器可能会抱怨说它们不认识`it`和`expect`这是因为它们缺少Jasmine的类型定义文件。
先暂且忽略这些抱怨,它们目前没有危害。
:marked
If we reload the browser, we should see the same Jasmine test-runner output as before.
如果我们刷新浏览器,我们会看到和以前一样的Jasmine测试运行器输出。
如果刷新浏览器,我们就会看到跟以前一样的Jasmine测试运行器输出。
Well be evolving these tests rapidly and it would be nice to have the browser refresh automatically as we make changes and recompile.
我们可以迅速进化这些测试,让浏览器在我们做出修改和重新编译时自动刷新的话就更方便了。
我们希望这些测试能快速进化,如果浏览器能在我们做出修改和重新编译时自动刷新就好了。
Lets launch with **live-server** in a second terminal window:
让我们在另一个终端窗口启动一个**live-server**。
没问题,让我们在另一个终端窗口中启动**live-server**
pre.prettyprint.lang-bash
code npm start
@ -185,37 +201,38 @@ pre.prettyprint.lang-bash
:marked
Now reload `unit-tests.html` in the browser
现在在浏览器刷新`unit-tests.html`
在浏览器刷新一下`unit-tests.html`
We should get the same Jasmine test-runner output as before.
我们应该能看到一样的Jasmine测试运行器输出。
我们应该能看到跟以前一样的Jasmine测试运行器输出。
.l-main-section
:marked
## Add a describe and another test
## 添加一个describe和其它测试
We cant tell what file produced these test results. We only have one file at the moment but soon well write more.
我们不知道什么文件生成了这些测试结果。虽然我们目前只有一个文件但是我们会编写更多
我们并不知道是哪个文件生成了这些测试结果。虽然目前我们只有一个文件但很快就会写更多文件
We should wrap this test into something that identifies the file. In Jasmine that “something” is a `describe` function.
Every test file should have at least one `describe` that identifies the file holding the test(s).
我们应该把这个测试包裹到一个可以识别这个文件的东西。在Jasmine里这个东西是一个`describe`函数。
每个测试文件都应该至少有一个`describe`来标识这个文件测试了什么。
我们应该把这个测试包裹进一个可以标识出所属文件的东西中。在Jasmine领域它就是`describe`函数。
每个测试文件都至少得有一个`describe`语句来标识该文件测试了什么。
Heres what our revised `1st.spec.ts` looks like when wrapped in a `describe`:
下面是我们修改`1st.spec.ts`,用`describe`包裹后的结果
下面是我们修改后的`1st.spec.ts`,用`describe`包裹后是这样的:
+makeExample('testing/ts/1st.spec.ts', 'describe')
:marked
And heres how the test report displays it.
下面是测试报告的显示:
下面是测试报告的显示内容
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/test-report-1-spec-0-failures.png' style="height:100px;" alt="1 spec, 0 failures")
@ -223,14 +240,14 @@ figure.image-display
:marked
Lets add another Jasmine test to `1st.spec.ts`
让我们添加另一个测试到`1st.spec.ts`
我们来向`1st.spec.ts`中添加另一个测试
+makeExample('testing/ts/1st.spec.ts', 'another-test')(format=".")
:marked
You knew that right? Lets prove it with this test. The browser should refresh after you paste that test, and show:
你知道这个吧?让我们用这个测试来证明。浏览器应该在你粘贴那个测试后更新,然后显示
你知道这个,对吧?现在让我们用这个测试来证明它。在你粘贴完那个测试后,浏览器会刷新并显示为
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/test-report-2-specs-0-failures.png' style="height:100px;" alt="refreshed 2 specs, 0 failures")
@ -238,14 +255,15 @@ figure.image-display
:marked
What does a failing test look like? Remove the `.not`. The browser refreshes and shows:
测试失败是什么样子?删除`.net`,刷新浏览器:
如果测试失败了会怎样?删除`.not`,浏览器会刷新并显示为:
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/test-report-2-specs-1-failure.png' style="height:190px;" alt="failing test 2 specs, 1 failure")
:marked
Click the `Spec List` link just below “2 specs, 1 failure” to see the summary again:
点击“2 Specs, 1 failure”下面的`Spec List`链接,看看总结
点击“2 Specs, 1 failure”下面的`Spec List`链接,再看看汇总信息
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/spec-list-2-specs-1-failure.png' style="height:140px;" alt="2 specs, 1 failure")
@ -253,31 +271,44 @@ figure.image-display
:marked
We can re-run just the failing test by double-clicking it. Try it!
我们可以只重新运行失败的测试,点击它就可以了。试试!
我们可以只重新运行那个失败的测试,双击它就可以了。试试看!
.l-main-section
:marked
## Debug the test
## 调试测试
## 调试那个测试
Suppose we didnt know what was going on. We can debug it in the browser.
假设我们不知道是怎么回事。我们都能在浏览器里面调试它。
假设我们不知道它为什么失败,就可以在浏览器中调试它。
- Open the browsers “Developer Tools” (F12 or Ctrl-Shift-I).
- 打开浏览器的“开发者工具”F12或者Ctrl-Shift-I)
- Pick the “sources” section
- 选择"sources"标签
- Open the `1st.spec.ts` test file (Ctrl-P, then start typing the name of the file).
- 打开`1st.spec.ts`测试文件Ctrl-P, 然后开始输入文件名字)
- 打开测试文件`1st.spec.ts`Ctrl-P, 然后开始输入文件名)
- Set a breakpoint on the second line of the failing test
- 在失败的测试的第二行,设置一个断点
- 在这个失败测试的第二行,设置一个断点
- Refresh the browser … and it stops at our breakpoint.
- 刷新浏览器...然后它停在我们的断点上
- 刷新浏览器...然后它停在了我们的断点上
- Open the console window at the bottom (press Esc)
- 在底部打开console窗口按Esc键
- 打开底部的console窗口按Esc键
- Type `null === undefined` … … and we should see this:
- 输入`null === undefined`...然后我们可以看到这个:
figure.image-display
@ -286,22 +317,23 @@ figure.image-display
:marked
How about that! They really arent equal.
你觉得怎么样?它们真的不相等。
怎么样?它们真的不相等。
- remove the breakpoint (right-click in the “Breakpoints” section and chose “Remove breakpoint”)
- 移除断点在“Breakpoints”区域右键点击“Remove breakpoint”)
- Click the “play” icon to resume the test (or F8)
- 点“play”图标继续运行测试或F8
- 点“play”图标继续运行测试或F8
And the test finishes. Close the browser tools (click the close box or press F12 or Ctrl-Shift-I)
然后测试完成。关闭浏览器工具点击关闭图标或者按F12或Ctrl-Shift-I)
这样测试就算完成了。关闭浏览器工具点击关闭图标或者按F12或Ctrl-Shift-I)
Fix the test (restore the `.not`); the browser should refresh automatically and all tests pass.
修复测试(恢复`.not`);浏览器应该自动刷新,所有测试都通过
修复测试(还原`.not`);浏览器会自动刷新,所有测试都通过了
Congratulations … youve completed Jasmine testing 101.
@ -309,7 +341,7 @@ figure.image-display
Now that were familiar with Jasmine on its own, were ready to test an application.
现在,我们熟悉了Jasmine的独立运作我们已做好准备测试一个应用程序了。
现在,我们已经熟悉了Jasmine本身接下来就要开始测试应用程序了。
<!-- TODO
.l-main-section

View File

@ -3,46 +3,89 @@ include ../_util-fns
:marked
Well test an Angular pipe in this chapter.
在本章中我们将测试一个Angular管道。
An Angular pipe is a declarative way in HTML to transform some input into some displayable output.
Angular管道是在HTML中用来声明把某些输入转换成某些可显示输出的方式。
We don't have a pipe though, since in Tour of Heroes we didn't create any pipes. It uses a pipe though, the `uppercase` pipe that comes with Angular 2.
我们还没有管道因为在《英雄指南》中我们没有创建过任何管道。不过它用过一个来自Angular 2中的`uppercase`管道。
We can make our own `my-uppercase` pipe that does exactly the same as the `uppercase` pipe and test that.
我们可以做一个自己的`my-uppercase`管道兵测试它,该管道的功能和`uppercase`管道完全相同。
Since we're getting ready to write some code we want to test, let's take this opportunity to talk just a little bit about [Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development). There's a lot written about this topic so we don't want to have an exhaustive description here, but rather a practical application.
既然我们准备写一些待测试的代码,那就趁机讲一点关于[测试驱动开发(TDD)](https://en.wikipedia.org/wiki/Test-driven_development)的知识吧。
关于这个主题已经有了大量的著述,我们这里也就不再全面描述它了,而只是针对一个实际的应用来讲。
We already know *exactly* what we want the `uppercase` pipe to do. We could say our ...expectations... of it are very well defined.
我们已经*确切的*知道`uppercase`应该做什么。可以说,我们对它的……预期行为……是有良好定义的(well defined)。
We always use expectations our expectations to guide development, but sometimes it's hard to see the forest for the trees when we're right in the middle of coding. This is especially evident in larger tasks.
So one thing we can do is put those expectations down as cold hard test code. We were going to test things manually anyway, so doing it *before* we have even one line of code isn't going to hurt.
我们总是使用自己的预期(Expectation)来指导开发,但是有时候,也很容易见木不见林,特别是我们的代码走到中途的时候。在大型任务中,这种情况尤为明显。
So one thing we can do is put those expectations down as cold hard test code. We were going to test things manually anyway, so doing it *before* we have even one line of code isn't going to hurt.
我们所能做的就是把这些预期落实到冰冷的测试代码上。反正无论如何我们都会手动测试它们,那么在连一行代码都没有的时候写点测试代码也不会带来什么问题。
Worst thing that can happen is have that test fail, but on the way to fixing it we'll end up creating our pipe. So in a sense, the failing test will *tell you what it wants* to pass.
最坏的结果也只是测试会失败,但在我们创建管道的过程中,会逐渐修复它。于是,在这个意义上,失败的测试会*告诉我们它要的是什么*。
We're just putting down expectations, nothing more. If we were to put them down on paper, they would look like this:
```
我们只要写下这些预期就行了,不用多。如果把它们写在纸上,大概是这样的:
```
MyUppercasePipe
transforms "abc" to "ABC"
transforms "abc def" to "ABC DEF"
leaves "ABC DEF" unchanged
```
All we need to know to put down our expectations as code is how a pipe class looks like from the outside. From the [pipe developer guide](pipes#custom-pipes) we know that a pipe implements a `transform` method.
```
All we need to know to put down our expectations as code is how a pipe class looks like from the outside. From the [pipe developer guide](pipes#custom-pipes) we know that a pipe implements a `transform` method.
我们需要知道的一切就是:把我们的预期用代码的形式写下来,就意味着这个管道类从外界看起来应该是什么样的。
从[管道开发指南](pipes#custom-pipes)中,我们知道管道实现了一个`transform`方法。
Putting it down as Jasmine expectations, they would look something like this:
+makeExample('testing/ts/app/my-uppercase.pipe.spec.ts', 'expectations')
把它写成Jasmine中的“预期”它们看起来是这样的
+makeExample('testing/ts/app/my-uppercase.pipe.spec.ts', 'expectations')
:marked
In this chapter we will:
在本章中,我们将:
- create a test before creating a class
- 在创建类之前创建一个测试
- load multiple test files in our test harness, using system.js
- 使用SystemJS把多个测试文件加载到我们的测试挽具中
- add the Angular 2 library to our test harness
- 往我们的测试挽具中添加Angular 2库
- watch the new test fail, and fix it
- 监视新测试,如果失败,就修复它
.callout.is-helpful
header Prior Knowledge
header 准备知识
:marked
The Unit Testing chapters build upon each other. We recommend reading them in order.
We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
@ -50,6 +93,9 @@ include ../_util-fns
the [Tour of Heroes](../tutorial/) tutorial
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
这一章单元测试是在其它章节的基础上写的。我们建议你按顺序阅读它们。同时我们假设你已经对Angular 2的原理、[“快速起步”](../quickstart.html)中介绍的工具、
[英雄指南](../tutorial)、<code>npm</code>、<code>gulp</code>和<code>live-server</code>等工具都已经有所了解。
:marked
## Add another spec file
@ -58,9 +104,9 @@ include ../_util-fns
**Stop and restart the TypeScript compiler** to ensure we compile the new file.
**Add** the following lines of rather obvious Jasmine test code.
+makeExample('testing/ts/app/my-uppercase.pipe.spec.ts', 'base-pipe-spec', 'app/my-uppercase.pipe.spec.ts')
:marked
Note that each test is short (one line in our case).
It has a clear label that accurately describes the test. And it makes exactly one expectation.
@ -84,7 +130,7 @@ include ../_util-fns
Fortunately, we can create a new `Promise` that wraps both import promises and waits
for both to finish loading.
+makeExample('testing/ts/unit-tests-4.html', 'promise-all')(format=".")
:marked
@ -92,7 +138,7 @@ include ../_util-fns
In future, when we add a new spec, we add another `System.import('app/some.spec')` to
the array argument passed to `Promise.all`.
Try it. The browser should refresh and show the following in the console:
code-example(format="" language="html" escape="html").
@ -100,23 +146,23 @@ code-example(format="" language="html" escape="html").
:marked
Our test failed, as expected. We're importing something that doesn't exist and our test fails saying that. All is going according to plan.
:marked
## The pipe, if you please
The test is asking for a pipe, and we shall deliver.
The test is asking for a pipe, and we shall deliver.
**Create** a `my-uppercase.pipe.ts` in `app/`.
**Stop and restart the TypeScript compiler** to ensure we compile the new file.
**Add** a basic pipe that doesn't do anything. We know how to make strings uppercase, but we since we're letting the test take the lead let's wait for it to tell us what's next. Maybe it'll surprise us.
+makeExample('testing/ts/app/my-uppercase.pipe.1.ts', null, 'app/my-uppercase.pipe.ts')
:marked
Reload our test page and...
code-example(format="" language="html" escape="html").
GET http://localhost:8080/angular2/core 404 (Not Found)
@ -129,7 +175,7 @@ code-example(format="" language="html" escape="html").
If it had depended on Angular, wed still be staring at the Jasmine “big-time fail” screen:
figure.image-display
img(src='/resources/images/devguide/testing-an-angular-pipe/big-time-fail-screen.png'
img(src='/resources/images/devguide/testing-an-angular-pipe/big-time-fail-screen.png'
alt="Jasmine's' big time fail screen")
:marked
@ -137,7 +183,7 @@ figure.image-display
we were going to need Angular sooner or later. That time has come.
`MyUppercasePipe` depends on Angular as is clear in the first few lines:
+makeExample('testing/ts/app/my-uppercase.pipe.ts', 'depends-on-angular')(format=".")
:marked
@ -151,42 +197,42 @@ figure.image-display
:marked
We should now be ready to see our 3 expectations fail when reloading our test page.
figure.image-display
img(src='/resources/images/devguide/testing-an-angular-pipe/two-failures.png' alt="2 failed tests")
:marked
## Uppercase, if you please
The first two tests that passed were our old `hero` interface tests, so it makes sense that those passed. Of our three new expectations, one still passed though.
```
```
MyUppercasePipe
transforms "abc" to "ABC"
transforms "abc def" to "ABC DEF"
leaves "ABC DEF" unchanged
```
```
Ah but of course! Our simple pipe doesn't transform the input at all, and the third test expected
input to not be changed.
All we have to do now is actually transform text to uppercase in our pipe.
+makeExample('testing/ts/app/my-uppercase.pipe.ts', 'uppercase')(format=".")
:marked
Are we done now?
figure.image-display
img(src='/resources/images/devguide/testing-an-angular-pipe/zero-failures.png' alt="0 failed tests")
:marked
The glorious green is back with us again!
We tried a bit of test driven development and it seems to have guided us to success.
But it's not always feasible. For instance, sometimes we need to write tests for existing functionality, like what we're about to do with the rest of Tour of Heroes.
But it's not always feasible. For instance, sometimes we need to write tests for existing functionality, like what we're about to do with the rest of Tour of Heroes.
If we are writing new code though, writing tests might just be what we need to help us track our progress and keep the end result in sight at all times.
:marked