-
-
Notifications
You must be signed in to change notification settings - Fork 26.9k
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
Document testing #538
Changes from 3 commits
d3a0aeb
728046e
976a754
a036aa7
b995398
ad0e062
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
|
@@ -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) | ||
|
@@ -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> | ||
|
@@ -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 | ||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too. "We did a major revamp to improve new user experience"? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's something we do in There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might even add |
||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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/): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 But, like I said, this might not be the right place for that... There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 😉 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
} | ||
``` | ||
|
||
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. | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it 😄