Skip to content

Commit

Permalink
Document testing (facebook#538)
Browse files Browse the repository at this point in the history
* Document testing

* Update README.md

* Update README.md

* Clarify our recommendations on testing

* Okay, that was too much. :-)

* Add a few more things
  • Loading branch information
gaearon authored and feiqitian committed Oct 25, 2016
1 parent 365d990 commit 7c2d214
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 5 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ my-app/
src/
App.css
App.js
App.test.js
index.css
index.js
logo.svg
Expand All @@ -76,6 +77,13 @@ You will see the build errors and lint warnings in the console.

<img src='https://camo.githubusercontent.com/41678b3254cf583d3186c365528553c7ada53c6e/687474703a2f2f692e696d6775722e636f6d2f466e4c566677362e706e67' width='600' alt='Build errors'>

### `npm test`

Runs the test watcher in an interactive mode.
By default, runs tests related to files changes since the last commit.

[Read more about testing.](template/README.md#running-tests)

### `npm run build`

Builds the app for production to the `build` folder.<br>
Expand Down Expand Up @@ -108,7 +116,7 @@ You can also read its latest version [here](https://github.com/facebookincubator
* Autoprefixed CSS, so you don’t need `-webkit` or other prefixes.
* A `build` script to bundle JS, CSS, and images for production, with sourcemaps.

**The feature set is intentionally limited**. It doesn’t support advanced features such as server rendering or CSS modules. Currently, it doesn’t support testing either. The tool is also **non-configurable** because it is hard to provide a cohesive experience and easy updates across a set of tools when the user can tweak anything.
**The feature set is intentionally limited**. It doesn’t support advanced features such as server rendering or CSS modules. The tool is also **non-configurable** because it is hard to provide a cohesive experience and easy updates across a set of tools when the user can tweak anything.

**You don’t have to use this.** Historically it has been easy to [gradually adopt](https://www.youtube.com/watch?v=BF58ZJ1ZQxY) React. However many people create new single-page React apps from scratch every day. We’ve heard [loud](https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4) and [clear](https://twitter.com/thomasfuchs/status/708675139253174273) that this process can be error-prone and tedious, especially if this is your first JavaScript build stack. This project is an attempt to figure out a good way to start developing React apps.

Expand All @@ -127,7 +135,6 @@ You don’t have to ever use `eject`. The curated feature set is suitable for sm
Some features are currently **not supported**:

* Server rendering.
* Testing.
* Some experimental syntax extensions (e.g. decorators).
* CSS Modules.
* LESS or Sass.
Expand All @@ -144,7 +151,8 @@ Currently it is a thin layer on top of many amazing community projects, such as:
* [Babel](http://babeljs.io/) with ES6 and extensions used by Facebook (JSX, [object spread](https://github.com/sebmarkbage/ecmascript-rest-spread/commits/master), [class properties](https://github.com/jeffmo/es-class-public-fields))
* [Autoprefixer](https://github.com/postcss/autoprefixer)
* [ESLint](http://eslint.org/)
* and more.
* [Jest](http://facebook.github.io/jest)
* and others.

All of them are transitive dependencies of the provided npm package.

Expand Down
216 changes: 214 additions & 2 deletions template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Folder Structure](#folder-structure)
- [Available Scripts](#available-scripts)
- [npm start](#npm-start)
- [npm test](#npm-test)
- [npm run build](#npm-run-build)
- [npm run eject](#npm-run-eject)
- [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor)
Expand All @@ -24,6 +25,17 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Integrating with a Node Backend](#integrating-with-a-node-backend)
- [Proxying API Requests in Development](#proxying-api-requests-in-development)
- [Adding `<meta>` Tags](#adding-meta-tags)
- [Running Tests](#running-tests)
- [Filename Conventions](#filename-conventions)
- [Command Line Interface](#command-line-interface)
- [Version Control Integration](#version-control-integration)
- [Writing Tests](#writing-tests)
- [Testing Components](#testing-components)
- [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries)
- [Coverage Reporting](#coverage-reporting)
- [Continuous Integration](#continuous-integration)
- [Disabling jsdom](#disabling-jsdom)
- [Experimental Snapshot Testing](#experimental-snapshot-testing)
- [Deployment](#deployment)
- [Now](#now)
- [Heroku](#heroku)
Expand Down Expand Up @@ -97,6 +109,11 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.

### `npm test`

Launches the test runner in the interactive watch mode.
See the section about [running tests](#running-tests) for more information.

### `npm run build`

Builds the app for production to the `build` folder.<br>
Expand Down Expand Up @@ -425,15 +442,15 @@ render() {
The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this
value, we need to have it defined in the environment:
### Windows (cmd.exe)
#### Windows (cmd.exe)
```cmd
set REACT_APP_SECRET_CODE=abcdef&&npm start
```
(Note: the lack of whitespace is intentional.)
### Linux, OS X (Bash)
#### Linux, OS X (Bash)
```bash
REACT_APP_SECRET_CODE=abcdef npm start
Expand Down Expand Up @@ -522,6 +539,201 @@ Then, on the server, regardless of the backend you use, you can read `index.html
If you use a Node server, you can even share the route matching logic between the client and the server. However duplicating it also works fine in simple cases.
## Running Tests
>Note: this feature is available with `[email protected]` and higher.
>[Read the migration guide to learn how to enable it in older projects!](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md#migrating-from-023-to-030)
Create React App uses [Jest](https://facebook.github.io/jest/) as its test runner. To prepare for this integration, we did a [major revamp](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html) of Jest so if you heard bad things about it, give it another try.
Jest is a Node-based runner. This means that the tests always run in a Node environment and not in a real browser. This lets us enable fast iteration speed and prevent flakiness.
While Jest provides browser globals such as `window` thanks to [jsdom](https://github.com/tmpvar/jsdom), they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks.
We recommend that you use a separate tool for browser end-to-end tests if you need them. They are beyond the scope of Create React App.
### Filename Conventions
Jest will look for test files with any of the following popular naming conventions:
* Files with `.js` suffix in `__tests__` folders.
* Files with `.test.js` suffix.
* Files with `.spec.js` suffix.
The `.test.js` / `.spec.js` files (or the `__tests__` folders) can be located at any depth under the `src` top level folder.
We recommend to put the test files (or `__tests__` folders) next to the code they are testing so that relative imports appear shorter. For example, if `App.test.js` and `App.js` are in the same folder, the test just needs to `import App from './App'` instead of a long relative path.
### Command Line Interface
When you run `npm test`, Jest will launch in the watch mode. Every time you save a file, it will re-run the tests, just like `npm start` recompiles the code.
The watcher includes an interactive command-line interface with the ability to run all tests, or focus on a search pattern. It is designed this way so that you could keep it open and enjoy fast re-runs. You can learn the commands from the “Watch Usage” note that the watcher prints after every run:
![Jest watch mode](http://facebook.github.io/jest/img/blog/15-watch.gif)
### Version Control Integration
By default, when you run `npm test`, Jest will only run the tests related to files changed since last commit. This is an optimization designed to make your tests runs fast regardless of how many tests you have. However it assumes that you don’t often commit the code that doesn’t pass the tests.
Jest will always explicitly mention that it only ran tests related to the files changed since the last commit. You can also press `a` in the watch mode to force Jest to run all tests.
Jest will always run all tests on a [continuous integration](#continuous-integration) server or if the project is not inside a Git or Mercurial repository.
### Writing Tests
To create tests, add `it()` blocks with the name of the test and its code. You may optionally wrap them in `describe()` blocks for logical grouping but this is neither required nor recommended.
Jest provides a built-in `expect()` global function for making assertions. A basic test could look like this:
```js
import sum from './sum';
it('sums numbers', () => {
expect(sum(1, 2)).toEqual(3);
expect(sum(2, 2)).toEqual(4);
});
```
All `expect()` matchers supported by Jest are [extensively documented here](http://facebook.github.io/jest/docs/api.html#expect-value).
You can also use [`jest.fn()` and `expect(fn).toBeCalled()`](http://facebook.github.io/jest/docs/api.html#tobecalled) to create “spies” or mock functions.
### Testing Components
There is a broad spectrum of component testing techniques. They range from a “smoke test” verifying that a component renders without throwing, to shallow rendering and testing some of the output, to full rendering and testing component lifecycle and state changes.
Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components:
```js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});
```
This test mounts a component and makes sure that it didn’t throw during rendering. Tests like this provide a lot value with very little effort so they are great as a starting point, and this is the test you will find in `src/App.test.js`.
When you encounter bugs caused by changing components, you will gain a deeper insight into which parts of them are worth testing in your application. This might be a good time to introduce more specific tests asserting specific expected output or behavior.
If you’d like to test components in isolation from the child components they render, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/). You can write a smoke test with it too:
```sh
npm install --save-dev enzyme react-addons-test-utils
```
```js
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
it('renders without crashing', () => {
shallow(<App />);
});
```
Unlike the previous smoke test using `ReactDOM.render()`, this test only renders `<App>` and doesn’t go deeper. For example, even if `<App>` itself renders a `<Button>` that throws, this test will pass. Shallow rendering is great for isolated unit tests, but you may still want to create some full rendering tests to ensure the components integrate correctly. Enzyme supports [full rendering with `mount()`](http://airbnb.io/enzyme/docs/api/mount.html), and you can also use it for testing state changes and component lifecyle.
You can read the [Enzyme documentation](http://airbnb.io/enzyme/) for more testing techniques. Enzyme documentation uses Chai and Sinon for assertions but you don’t have to use them because Jest provides built-in `expect()` and `jest.fn()` for spies.
Here is an example from Enzyme documentation that asserts specific output, rewritten to use Jest matchers:
```js
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
it('renders welcome message', () => {
const wrapper = shallow(<App />);
const welcome = <h2>Welcome to React</h2>;
// expect(wrapper.contains(welcome)).to.equal(true);
expect(wrapper.contains(welcome)).toEqual(true);
});
```
All Jest matchers are [extensively documented here](http://facebook.github.io/jest/docs/api.html#expect-value).
Nevertheless you can use a third-party assertion library like Chai if you want to, as described below.
### Using Third Party Assertion Libraries
We recommend that you use `expect()` for assertions and `jest.fn()` for spies. If you are having issues with them please [file those against Jest](https://github.com/facebook/jest/issues/new), and we’ll fix them. We intend to keep making them better for React, supporting, for example, [pretty-printing React elements as JSX](https://github.com/facebook/jest/pull/1566).
However, if you are used to other libraries, such as [Chai](http://chaijs.com/) and [Sinon](http://sinonjs.org/), or if you have existing code using them that you’d like to port over, you can import them normally like this:
```js
import sinon from 'sinon';
import { expect } from 'chai';
```
and then use them in your tests like you normally do.
### Coverage Reporting
Jest has an integrated coverage reporter that works well with ES6 and requires no configuration.
Run `npm test -- --coverage` (note extra `--` in the middle) to include a coverage report like this:
![coverage report](http://i.imgur.com/5bFhnTS.png)
Note that tests run much slower with coverage so it is recommended to run it separately from your normal workflow.
### Continuous Integration
By default `npm test` runs the watcher with interactive CLI. However, you can force it to run tests once and finish the process by setting an environment variable called `CI`. Popular CI servers already set it by default but you can do this yourself too:
#### Windows (cmd.exe)
```cmd
set CI=true&&npm test
```
(Note: the lack of whitespace is intentional.)
#### Linux, OS X (Bash)
```bash
CI=true npm test
```
This way Jest will run tests once instead of launching the watcher.
If you find yourself doing this often in development, please [file an issue](https://github.com/facebookincubator/create-react-app/issues/new) to tell us about your use case because we want to make watcher the best experience and are open to changing how it works to accommodate more workflows.
### Disabling jsdom
By default, the `package.json` of the generated project looks like this:
```js
// ...
"scripts": {
// ...
"test": "react-scripts test --env=jsdom"
}
```
If you know that none of your tests depend on [jsdom](https://github.com/tmpvar/jsdom), you can safely remove `--env=jsdom`, and your tests will run faster.
To help you make up your mind, here is a list of APIs that **need jsdom**:
* Any browser globals like `window` and `document`
* [`ReactDOM.render()`](https://facebook.github.io/react/docs/top-level-api.html#reactdom.render)
* [`TestUtils.renderIntoDocument()`](https://facebook.github.io/react/docs/test-utils.html#renderintodocument) ([a shortcut](https://github.com/facebook/react/blob/34761cf9a252964abfaab6faf74d473ad95d1f21/src/test/ReactTestUtils.js#L83-L91) for the above)
* [`mount()`](http://airbnb.io/enzyme/docs/api/mount.html) in [Enzyme](http://airbnb.io/enzyme/index.html)
In contrast, **jsdom is not needed** for the following APIs:
* [`TestUtils.createRenderer()`](https://facebook.github.io/react/docs/test-utils.html#shallow-rendering) (shallow rendering)
* [`shallow()`](http://airbnb.io/enzyme/docs/api/shallow.html) in [Enzyme](http://airbnb.io/enzyme/index.html)
Finally, jsdom is also not needed for [snapshot testing](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html). Longer term, this is the direction we are interested in exploring, but snapshot testing is [not fully baked yet](https://github.com/facebookincubator/create-react-app/issues/372) so we don’t officially encourage its usage yet.
### Experimental Snapshot Testing
Snapshot testing is a new feature of Jest that automatically generates text snapshots of your components and saves them on the disk so if the UI output changes, you get notified without manually writing any assertions on the component output.
This feature is experimental and still [has major usage issues](https://github.com/facebookincubator/create-react-app/issues/372) so we only encourage you to use it if you like experimental technology. We intend to gradually improve it over time and eventually offer it as the default solution for testing React components, but this will take time. [Read more about snapshot testing.](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html)
## Deployment
By default, Create React App produces a build assuming your app is hosted at the server root.
Expand Down

0 comments on commit 7c2d214

Please sign in to comment.