Skip to content

Commit

Permalink
docs(tests): add some more examples in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Kent C. Dodds authored and alexkrolick committed Sep 13, 2018
1 parent 3ff74bb commit a6c2f2e
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 1 deletion.
12 changes: 12 additions & 0 deletions packages/react-testing-library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ components. It provides light utility functions on top of `react-dom` and
* [`flushPromises`](#flushpromises)
* [`render`](#render)
* [More on `data-testid`s](#more-on-data-testids)
* [Examples](#examples)
* [FAQ](#faq)
* [Other Solutions](#other-solutions)
* [Guiding Principles](#guiding-principles)
Expand Down Expand Up @@ -144,6 +145,17 @@ one of the practices this library is intended to encourage.
Learn more about this practice in the blog post:
["Making your UI tests resilient to change"](https://blog.kentcdodds.com/making-your-ui-tests-resilient-to-change-d37a6ee37269)

## Examples

You'll find examples of testing with different libraries in
[the test directory](https://github.com/kentcdodds/react-testing-library/blob/master/src/__tests__).
Some included are:

* [`react-redux`](https://github.com/kentcdodds/react-testing-library/blob/master/src/__tests__/react-redux.js)
* [`react-router`](https://github.com/kentcdodds/react-testing-library/blob/master/src/__tests__/react-router.js)

Feel free to contribute more!

## FAQ

**How do I update the props of a rendered component?**
Expand Down
7 changes: 6 additions & 1 deletion packages/react-testing-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@
"dependencies": {},
"devDependencies": {
"axios": "^0.18.0",
"history": "^4.7.2",
"kcd-scripts": "^0.36.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-transition-group": "^2.2.1"
"react-redux": "^5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-transition-group": "^2.2.1",
"redux": "^3.7.2"
},
"peerDependencies": {
"react-dom": "*"
Expand Down
108 changes: 108 additions & 0 deletions packages/react-testing-library/src/__tests__/react-redux.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React from 'react'
import {createStore} from 'redux'
import {Provider, connect} from 'react-redux'
import {render, Simulate} from '../'

// counter.js
class Counter extends React.Component {
increment = () => {
this.props.dispatch({type: 'INCREMENT'})
}

decrement = () => {
this.props.dispatch({type: 'DECREMENT'})
}

render() {
return (
<div>
<h2>Counter</h2>
<div>
<button onClick={this.decrement} data-testid="decrementer">
-
</button>
<span data-testid="count-value">{this.props.count}</span>
<button onClick={this.increment} data-testid="incrementer">
+
</button>
</div>
</div>
)
}
}

// normally this would be:
// export default connect(state => ({count: state.count}))(Counter)
// but for this test we'll give it a variable name
// because we're doing this all in one file
const ConnectedCounter = connect(state => ({count: state.count}))(Counter)

// app.js
function reducer(state = {count: 0}, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1,
}
case 'DECREMENT':
return {
count: state.count - 1,
}
default:
return state
}
}

// normally here you'd do:
// const store = createStore(reducer)
// ReactDOM.render(
// <Provider store={store}>
// <Counter />
// </Provider>,
// document.getElementById('root'),
// )
// but for this test we'll umm... not do that :)

// Now here's what your test will look like:

// this is a handy function that I normally make available for all my tests
// that deal with connected components.
// you can provide initialState or the entire store that the ui is rendered with
function renderWithRedux(
ui,
{initialState, store = createStore(reducer, initialState)} = {},
) {
return {
...render(<Provider store={store}>{ui}</Provider>),
// adding `store` to the returned utilities to allow us
// to reference it in our tests (just try to avoid using
// this to test implementation details).
store,
}
}

test('can render with redux with defaults', () => {
const {queryByTestId} = renderWithRedux(<ConnectedCounter />)
Simulate.click(queryByTestId('incrementer'))
expect(queryByTestId('count-value').textContent).toBe('1')
})

test('can render with redux with custom initial state', () => {
const {queryByTestId} = renderWithRedux(<ConnectedCounter />, {
initialState: {count: 3},
})
Simulate.click(queryByTestId('decrementer'))
expect(queryByTestId('count-value').textContent).toBe('2')
})

test('can render with redux with custom store', () => {
// this is a silly store that can never be changed
const store = createStore(() => ({count: 1000}))
const {queryByTestId} = renderWithRedux(<ConnectedCounter />, {
store,
})
Simulate.click(queryByTestId('incrementer'))
expect(queryByTestId('count-value').textContent).toBe('1000')
Simulate.click(queryByTestId('decrementer'))
expect(queryByTestId('count-value').textContent).toBe('1000')
})
73 changes: 73 additions & 0 deletions packages/react-testing-library/src/__tests__/react-router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react'
import {withRouter} from 'react-router'
import {Link, Route, Router, Switch} from 'react-router-dom'
import {createMemoryHistory} from 'history'
import {render, Simulate} from '../'

const About = () => <div>You are on the about page</div>
const Home = () => <div>You are home</div>
const NoMatch = () => <div>No match</div>

const LocationDisplay = withRouter(({location}) => (
<div data-testid="location-display">{location.pathname}</div>
))

function App() {
return (
<div>
<Link to="/" data-testid="home-link">
Home
</Link>
<Link to="/about" data-testid="about-link">
About
</Link>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route component={NoMatch} />
</Switch>
<LocationDisplay />
</div>
)
}

// Ok, so here's what your tests might look like

// this is a handy function that I would utilize for any component
// that relies on the router being in context
function renderWithRouter(
ui,
{route = '/', history = createMemoryHistory({initialEntries: [route]})} = {},
) {
return {
...render(<Router history={history}>{ui}</Router>),
// adding `history` to the returned utilities to allow us
// to reference it in our tests (just try to avoid using
// this to test implementation details).
history,
}
}

test('full app rendering/navigating', () => {
const {container, queryByTestId} = renderWithRouter(<App />)
// normally I'd use a data-testid, but just wanted to show this is also possible
expect(container.innerHTML).toMatch('You are home')
const leftClick = {button: 0}
Simulate.click(queryByTestId('about-link'), leftClick)
// normally I'd use a data-testid, but just wanted to show this is also possible
expect(container.innerHTML).toMatch('You are on the about page')
})

test('landing on a bad page', () => {
const {container} = renderWithRouter(<App />, {
route: '/something-that-does-not-match',
})
// normally I'd use a data-testid, but just wanted to show this is also possible
expect(container.innerHTML).toMatch('No match')
})

test('rendering a component that uses withRouter', () => {
const route = '/some-route'
const {queryByTestId} = renderWithRouter(<LocationDisplay />, {route})
expect(queryByTestId('location-display').textContent).toBe(route)
})

0 comments on commit a6c2f2e

Please sign in to comment.