Testing our code gives us: • confidence • permanently fix bugs by fixing them with a test • provide support for refactoring • enables teams to move quicker 5
JavaScript Testing Libraries I prefer Tape [https://github.com/substack/tape], but there's also: • Mocha • Jasmine • Jest • ... This talk is test framework agnostic :) 7
• Tell tape how many tests you're expecting with t.plan(x) • Using t.plan means Tape has good async support out the box • Use t.equal, t.ok, t.deepEqual (and others) • Has good support for Babel • Fairly minimal :) 10
> tape -r babel-register test/*-test.js TAP version 13 # Adding numbers # 2 + 2 is 4 ok 1 should be equal # some objects are equal ok 2 should be equivalent 1..2 # tests 2 # pass 2 # ok 13
Enter Faucet npm install --save-dev faucet "scripts": { "test": "tape -r babel-register test/*-test.js | faucet" }, There are many, many options here for this! 15
state-functions.js export function toggleDone(state, id) { ... } export function addTodo(state, todo) { ... } export function deleteTodo(state, id) { ... } (See also: Redux and related projects) 29
• The rendered div is given a class of done-todo if the todo is done. • Deleting a todo calls this.props.deleteTodo with the right ID. • Clicking on a todo calls this.props.toggleDone with the right ID. 35
Shallow Rendering Test without a DOM; we don't actually render components but get a representation of what the rendered component will look like. Keeps tests nice and quick :) 37
todo-test.js import React from 'react'; import Todo from '../app/todo'; import TestUtils from 'react-addons-test-utils'; import test from 'tape'; function shallowRenderTodo(todo) { const renderer = TestUtils.createRenderer(); const fn = () => {}; renderer.render(
React components will often have some state (like our todos) that users can edit. Input: User Actions (clicks, form fills, etc) Output: Rendered React components Never reach into a component to get or set state. 43
Rendering a component for testing TestUtils.renderIntoDocument(...) Render a component into a detached DOM node in the document. [https://facebook.github.io/react/docs/test- utils.html] 49
t.test('toggling a TODO calls the given prop', (t) => { t.plan(1); // our assertion is made in the doneCallback fn // so when the click is made, it will be run const doneCallback = (id) => t.equal(id, 1); const todo = { id: 1, name: 'Buy Milk', done: false }; const result = TestUtils.renderIntoDocument(
t.test('toggling a TODO calls the given prop', (t) => { t.plan(1); const doneCallback = (id) => t.equal(id, 1); const todo = { id: 1, name: 'Buy Milk', done: false }; const result = TestUtils.renderIntoDocument(
); const todoText = TestUtils.findRenderedDOMComponentWithTag(result, 'p'); TestUtils.Simulate.click(todoText); }); Some people don't like this callback approach to testing. 54
A double is just a function that keeps track of its calls: import { Double } from 'doubler'; const x = Double.function(); x('react'); x('amsterdam'); x.callCount === 2; x.args === [ ['react'], ['amsterdam'] ]; 59
We first render AddTodo, giving it a double as a callback: test('Add Todo component', (t) => { t.test('it calls the given callback prop with the new text', (t) => { t.plan(2); const todoCallback = Double.function(); const form = TestUtils.renderIntoDocument(
Then we can find and fill out the form input: const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); input.value = 'Buy Milk'; Before then clicking a button: const button = TestUtils.findRenderedDOMComponentWithTag(form, 'button'); TestUtils.Simulate.click(button); 68
test('Add Todo component', (t) => { t.test('it calls the given callback prop with the new text', (t) => { t.plan(2); const todoCallback = Double.function(); const form = TestUtils.renderIntoDocument(
scryRenderedDOMComponentsWithClass finds all components with the given class, and returns an array. Scrying (also called seeing or peeping) is the practice of looking into a translucent ball 77
Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output. Enzyme's API is meant to be intuitive and flexible by mimicking jQuery's API for DOM manipulation and traversal. 81
Enzyme is unopinionated regarding which test runner or assertion library you use, and should be compatible with all major test runners and assertion libraries out there. 82
browserify test/*-test.js \ -t [ babelify --presets [ es2015 react ] ] \ -u 'react/lib/ReactContext' \ -u 'react/lib/ExecutionEnvironment' \ | tape-run -b chrome Tests are run in an actual browser instance; results are sent back to the terminal. 94
Blog post: http://12devsofxmas.co.uk/2015/12/ day-2-testing-react-applications/ Repo with Tests: https://github.com/jackfranklin/ todo-react-testing Slides (in a bit): speakerdeck.com/jackfranklin Me: @Jack_Franklin, javascriptplayground.com 98