angular-cn/public/docs/ts/latest/testing/first-app-tests.jade

256 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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测试。
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`指令运行测试
- load the test file with SystemJS
- 使用SystemJs加载测试文件
.callout.is-helpful
header Prior Knowledge 预先需要的知识
: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
we introduced in the [QuickStart](../quickstart.html) and
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>等工具都所有了解。
.l-main-section
:marked
## Create the 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`的目录。
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`里面的基础内容拷贝到此文件中。
+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`
We must install the Jasmine package as well:
我们也必须安装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>! 请注意,安装<code>jasmine-core</code>,而不是<code>jasmine</code>!
:marked
Let's make one more change to the `package.json` script commands.
让我们在`package.json`的脚本命令部分再修改一项。
**Open the `package.json` ** and scroll to the `scripts` node and add in a new one:
**打开`package.json`**,滚动到`scripts`节点,添加下面这行:
code-example(format="").
"test": "live-server --open=unit-tests.html"
: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`页面。
.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`接口:
+makeExample('testing/ts/app/hero.ts')
:marked
Let's add a couple of simple tests in a new file.
让我们在一个新文件里面添加一些简单的测试。
+makeExample('testing/ts/app/hero.spec.ts', 'base-hero-spec')
:marked
That's the basic Jasmine we learned back in "Jasmine 101".
上面是我们在“Jasmine 101”学习的基本Jasmine知识。
Notice that we surrounded our tests with ** `describe('Hero')` **.
请注意,我们把我们的测试围在**`describe('Hero')`**里面。
**By convention, our test always begin with a `describe` that identifies the application part under test.**
**按惯例,我们的测试都已`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.
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`.
Notice the ".spec" suffix in the test file's filename, appended to the name of the file holding the application part we're testing.
.alert.is-important All of our unit test files follow this .spec naming pattern.
:marked
Save the tests we just made in `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.
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.
### Update 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:
+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
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_.**"
Open the browser's Developer Tools (F12, Ctrl-Shift-i). There's an error:
code-example(format="" language="html").
Uncaught ReferenceError: System is not defined
.l-main-section
:marked
## Load tests with 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.
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.
We are committed to module loading in our application.
Our app will call `import`. Our tests must do so too.
We add module loading support in four steps:
1. add the *SystemJS* module management library
1. configure *SystemJS* to look for JavaScript files by default
1. import our test files
1. tell Jasmine to run the imported tests
These steps are all clearly visible, in exactly that order, in the following lines that
replace the `<body>` contents in `unit-tests.html`:
+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")
.l-main-section
: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.
Once configured with a default extension of 'js',&nbsp; SystemJS requests `hero.js` which *does* exist and is promptly returned by our server.
### Asynchronous 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.
### 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.
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.
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.
.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.
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.