Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document testing #538

Merged
merged 6 commits into from
Sep 1, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) (it’s [not bad anymore!](http://facebook.github.io/jest/blog/2016/09/01/jest-15.html))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, I'm not sure what to think about this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, yeah, perhaps changing the wording to be more kind and positive. Afterall, real people worked hard on Jest before too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, the thing is, most people that will read this have never heard of Jest and this gives them a negative impression. The people that knew about Jest being terrible are the ones that used React years ago already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also say "Jest" and highlight the two latest release blog posts with explanation of great features.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed it 😄

* and others.

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

Expand Down
195 changes: 193 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,16 @@ 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)
- [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 +108,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 +441,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 +538,181 @@ 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.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too. "We did a major revamp to improve new user experience"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we not? “Jest is bad” is well and alive, no need to look further than the recent issue. I do think we need to address this concern in text. I don’t think it makes us look bad, just honest about past problems and our willingness to solve them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that makes sense. Can we maybe say "The only resemblance of Jest with the test framework from a year ago is its name?" That sounds more uplifting. I just wanna avoid giving people a bad impression if they haven't even heard about Jest before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s discuss in a follow up PR if you’d like to make a pass over this. But as you know, it’s better to under-promise and over-deliver. I would give it a few months and then revisit when the perception is turned around thanks to word of mouth.


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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we add "If you'd like to run real end-to-end tests, try selenium" or something? We should totally add a photo of the testing pyramid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 as a follow up but I already spent a ton of time on this so will ship as is for now


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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, there it is.


### 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also good to colocate tests with the modules they test so that they are easy to locate in larger projects.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


### 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.
Copy link
Contributor

@kentcdodds kentcdodds Sep 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend against watch mode being the default. Many CI systems will run npm test by default. In my experience, it's more common to have npm test run the tests once (with --coverage) and have a watch:test script.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh dang. I just saw the note about how jest is smart and wont watch on CI... That's a super feature!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's something we do in react-scripts as suggested by @ForbesLindesay but maybe Jest could do that by default! cc @cpojer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty amazing what you can do when you just forward npm scripts onto your own binary :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dan: wanna send a PR for that? I'm happy to ship that as a default. As we expand interactive watch, there are probably few reasons to not use it as a default.


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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"you can keep it open"?


![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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the last commit.


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 it not required.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might even add (nor is it recommended). Here's why I feel that way..


Jest provides a built-in `expect()` global 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().to*` matchers supported by Jest are [extensively documented here](http://facebook.github.io/jest/docs/api.html#expect-value). You can [use `jest.fn()` and `expect(fn).toBeCalled()`](http://facebook.github.io/jest/docs/api.html#tobecalled) to create “spies” or mock functions.

### Testing Components
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also highlight that people shouldn't waste time writing tests for components while they don't even know what the component is gonna be like? I don't want to be too prescriptive but component testing and especially snapshot testing is a way for me to lock in behavior that I carefully designed previously. This way I get maximum time and focus building the thing I want and minimal time creating a test (just render + toMatchSnapshot or whatever).

This is totally the opposite for store/reducer tests and I think it is worth mentioning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s do all of this as follow up, I really want to ship now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw I’d be grateful if you could just do a pass over all of this in one PR and change anything you like


Generally we recommend to test complex logic instead of testing component output. In most components apps component output changes too often to be useful in testing.

Often, a “smoke test” that just mounts a component and makes sure that it didn’t throw during rendering gives the most value with the least effort. You can find a test like this called `App.test.js` in your `src` folder by default:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest smoke testing as "a good start" and omit the bit about most value/least effort (even though it's true). I think as its worded, beginners might take it to mean "smoke testing is all I need."

Maybe follow up by showing an example of a "good" behavioral integration test (use component selectors in enzyme, simulate some events, then make assertions on UI changes that aren't too coupled to component implementation).

Glad to see create-react-app start to encourage better testing practices!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@btford Appreciate your feedback. Reworded the section.


```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);
});
```

If you’d like to test components individually without child components affecting them, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this caveat should go in the enzyme docs instead, but it might be useful to note that shallow rendering is limited in the sense that if you have private components in the module you're testing, you wont be able to assert on anything those private components do. This is one of the reasons I rarely do shallow rendering personally.

But, like I said, this might not be the right place for that...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the target audience is newcomers to testing, giving some examples of when you'd use this strategy might be better than explaining the mechanics behind how shallow works and its tradeoffs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely see us improving this guide in the future with community input. For now I’ll ship this with some changes I just made. Thanks for feedback!


```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 />);
});
```

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, 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 />);
// expect(wrapper.contains(<h2>Welcome to React</h2>)).to.equal(true);
expect(wrapper.contains(<h2>Welcome to React</h2>)).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.

### 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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting! I haven't followed CRA too closely, but this appears to be the first instance of anything that resembles config. Is this the decided upon method of config? Is this something to be concerned about? I realize that this is a pretty useful optimization. Just wanted to call it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s mostly there to stick out so people are incentivized to figure out ways to test their stuff without jsdom. 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, as mentioned, we’ll be recommending snapshot testing, and when it becomes the default test we ship, we’ll remove env=jsdom from the default test script.

}
```

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