Test Driven Development with React Leon Bezuidenhout 🦁🐝 hello@lionbee.net @lionbeesays medium.com/@lionbee
Quick survey ✋ Raise your hand if you wrote code today ✋ Keep your hand raised if you wrote unit tests ✋ Keep your hand raised if you did TDD
Overview ● What is software testing ● The tools I use ● What is TDD (Test Driven Development) ● Example of TDD ● Take away
What is software testing? Software testing is an empirical technical investigation conducted to provide stakeholders with information about the quality of the product or service under test Kaner, Cem (November 17, 2006). Exploratory Testing (PDF). Retrieved from http://www.kaner.com/pdfs/ETatQAI.pdf
Testing levels Ham Vocke (26 February 2018). The Practical Test Pyramid. Retrieved from https://martinfowler.com/articles/practical-test- pyramid.html
Reasons for writing unit tests ● Ensure expected behavior ● Serves as documentation ● Automated regression
Tools for testing Jest + Enzyme
it("Caption is set", () => { const caption = 'test'; const wrapper = shallow( <AwesomeButton caption={caption} /> ); expect(wrapper.text()).toEqual(caption); }); Example unit test
What is Test Driven Development? It is an iterative programming workflow whereby you write a single unit test and then write the code, if required, to make the test pass. When the test passes you refactor your code, including the tests, before writing the next test.
TDD workflow
TDD workflow
Dan is correct
TDD only works if you have a design
Writing code is a valid way to design Remember to add tests and refactor
Trivial TDD Example Build a button component using React that has a configurable caption and on click event.
Environment » create-react-app awesome-button --typescript » cd awesome-button » npm i -d enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme- adapter-react-16 //setupTests.ts import { configure } from "enzyme"; import Adapter from "enzyme-adapter-react-16"; configure({ adapter: new Adapter() }); » touch src/AwesomeButton.test.tsx
FAIL src/AwesomeButton.test.tsx ● Test suite failed to run Your test suite must contain at least one test. » npm test
// AwesomeButton.test.tsx describe('AwesomeButton test suite', () => { it('Environment works', ()=> {}) }); PASS src/awesomebutton.test.tsx AwesomeButton test suite ✓ Environment works (2ms) Test Suites: 1 passed, 1 total
import * as React from 'react'; import { shallow } from "enzyme"; import { AwesomeButton } from "./AwesomeButton"; describe("AwesomeButton test suite", () => { it("Renders", () => { shallow(<AwesomeButton />); }); }); FAIL src/AwesomeButton.test.tsx > 4 | import { AwesomeButton } from "./AwesomeButton"; | ^
// AwesomeButton.tsx import * as React from 'react'; export function AwesomeButton() { return <button />; } PASS src/awesomebutton.test.ts AwesomeButton test suite ✓ Renders (5ms) Test Suites: 1 passed, 1 total
it("Caption is set", () => { const caption = 'test'; const wrapper = shallow( <AwesomeButton caption={caption} /> ); expect(wrapper.text()).toEqual(caption); }); ✕ Caption is set (5ms) ● AwesomeButton test suite › Caption is set expect(received).toEqual(expected) // deep equality Expected: "test" Received: ""
interface AwesomeButtonProps { caption?: string; } export function AwesomeButton(props: AwesomeButtonProps) { return <button>{props.caption}</button>; } ✓ Caption is set (2ms)
it("onClick handler works", () => { const mockClick = jest.fn(); const wrapper = shallow( <AwesomeButton onClick={mockClick} /> ); wrapper.simulate('click'); expect(mockClick).toHaveBeenCalledTimes(1); }); ✕ onClick handler works (47ms) ● AwesomeButton test suite › onClick handler works expect(jest.fn()).toHaveBeenCalledTimes(expected) Expected number of calls: 1 Received number of calls: 0
interface AwesomeButtonProps { caption?: string; onClick?: () => void; } export function AwesomeButton(props: AwesomeButtonProps) { return <button onClick={props.onClick}> {props.caption} </button>; } ✓ onClick handler works (1ms)
it("Clicking with no handler does not raise errors", () => { const wrapper = shallow( <AwesomeButton /> ); wrapper.simulate('click'); }); ✓ Clicking with no handler does not raise errors
Fixing
it("Caption is set", () => { const caption = 'test'; const wrapper = shallow( <AwesomeButton caption={caption} /> ); const captionContainer = wrapper .find('[data-awesomebutton="caption"]'); expect(captionContainer.text()).toEqual(caption); }); ✕ Caption is set (44ms) ● AwesomeButton test suite › Caption is set Method “text” is meant to be run on 1 node. 0 found instead. 16 | const captionContainer = wrapper 17 | .find('[data-awesomebutton="caption"]'); > 18 | expect(captionContainer.text()).toEqual(caption); | ^
export function AwesomeButton(props: AwesomeButtonProps) { return <button onClick={props.onClick}> <span data-awesomebutton='caption'> {props.caption} </span> </button>; } ✓ Caption is set (2ms)
» npm test -- --coverage PASS src/AwesomeButton.test.tsx AwesomeButton test suite ✓ Renders (5ms) ✓ Caption is set (5ms) ✓ onClick handler works (2ms) ✓ Clicking with no handler does not raise errors -------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------|----------|----------|----------|----------|-------------------| All files | 100 | 100 | 100 | 100 | | AwesomeButton.tsx | 100 | 100 | 100 | 100 | | -------------------|----------|----------|----------|----------|-------------------| Test Suites: 1 passed, 1 total Tests: 4 passed, 4 total Snapshots: 0 total Time: 3.607s
Advantages ● It feels strangely natural ● It can up your engagement ● It’s a good way to pick up an old toolset ● It’s a great way to learn a new language ● It improves design ● It improves estimates ● It’s excellent for pair programming ● When your feature is done, so are your tests
Practice A code kata is an exercise in programming which helps programmers hone their skills through practice and repetition. 🔍 TDD code kata javascript
Take this with you ● Testing is as important as the feature ● Test code is as important as feature code ● TDD is about iterative programming ● TDD may make you a better programmer ● Practice ● Be pragmatic
Thank you

Test driven development with react

  • 1.
    Test Driven Development withReact Leon Bezuidenhout 🦁🐝 hello@lionbee.net @lionbeesays medium.com/@lionbee
  • 2.
    Quick survey ✋ Raiseyour hand if you wrote code today ✋ Keep your hand raised if you wrote unit tests ✋ Keep your hand raised if you did TDD
  • 3.
    Overview ● What issoftware testing ● The tools I use ● What is TDD (Test Driven Development) ● Example of TDD ● Take away
  • 4.
    What is softwaretesting? Software testing is an empirical technical investigation conducted to provide stakeholders with information about the quality of the product or service under test Kaner, Cem (November 17, 2006). Exploratory Testing (PDF). Retrieved from http://www.kaner.com/pdfs/ETatQAI.pdf
  • 5.
    Testing levels Ham Vocke(26 February 2018). The Practical Test Pyramid. Retrieved from https://martinfowler.com/articles/practical-test- pyramid.html
  • 6.
    Reasons for writingunit tests ● Ensure expected behavior ● Serves as documentation ● Automated regression
  • 7.
  • 8.
    it("Caption is set",() => { const caption = 'test'; const wrapper = shallow( <AwesomeButton caption={caption} /> ); expect(wrapper.text()).toEqual(caption); }); Example unit test
  • 9.
    What is TestDriven Development? It is an iterative programming workflow whereby you write a single unit test and then write the code, if required, to make the test pass. When the test passes you refactor your code, including the tests, before writing the next test.
  • 10.
  • 11.
  • 13.
  • 14.
    TDD only worksif you have a design
  • 15.
    Writing code isa valid way to design Remember to add tests and refactor
  • 16.
    Trivial TDD Example Builda button component using React that has a configurable caption and on click event.
  • 17.
    Environment » create-react-app awesome-button--typescript » cd awesome-button » npm i -d enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme- adapter-react-16 //setupTests.ts import { configure } from "enzyme"; import Adapter from "enzyme-adapter-react-16"; configure({ adapter: new Adapter() }); » touch src/AwesomeButton.test.tsx
  • 18.
    FAIL src/AwesomeButton.test.tsx ● Testsuite failed to run Your test suite must contain at least one test. » npm test
  • 19.
    // AwesomeButton.test.tsx describe('AwesomeButton testsuite', () => { it('Environment works', ()=> {}) }); PASS src/awesomebutton.test.tsx AwesomeButton test suite ✓ Environment works (2ms) Test Suites: 1 passed, 1 total
  • 20.
    import * asReact from 'react'; import { shallow } from "enzyme"; import { AwesomeButton } from "./AwesomeButton"; describe("AwesomeButton test suite", () => { it("Renders", () => { shallow(<AwesomeButton />); }); }); FAIL src/AwesomeButton.test.tsx > 4 | import { AwesomeButton } from "./AwesomeButton"; | ^
  • 21.
    // AwesomeButton.tsx import *as React from 'react'; export function AwesomeButton() { return <button />; } PASS src/awesomebutton.test.ts AwesomeButton test suite ✓ Renders (5ms) Test Suites: 1 passed, 1 total
  • 22.
    it("Caption is set",() => { const caption = 'test'; const wrapper = shallow( <AwesomeButton caption={caption} /> ); expect(wrapper.text()).toEqual(caption); }); ✕ Caption is set (5ms) ● AwesomeButton test suite › Caption is set expect(received).toEqual(expected) // deep equality Expected: "test" Received: ""
  • 23.
    interface AwesomeButtonProps { caption?:string; } export function AwesomeButton(props: AwesomeButtonProps) { return <button>{props.caption}</button>; } ✓ Caption is set (2ms)
  • 24.
    it("onClick handler works",() => { const mockClick = jest.fn(); const wrapper = shallow( <AwesomeButton onClick={mockClick} /> ); wrapper.simulate('click'); expect(mockClick).toHaveBeenCalledTimes(1); }); ✕ onClick handler works (47ms) ● AwesomeButton test suite › onClick handler works expect(jest.fn()).toHaveBeenCalledTimes(expected) Expected number of calls: 1 Received number of calls: 0
  • 25.
    interface AwesomeButtonProps { caption?:string; onClick?: () => void; } export function AwesomeButton(props: AwesomeButtonProps) { return <button onClick={props.onClick}> {props.caption} </button>; } ✓ onClick handler works (1ms)
  • 26.
    it("Clicking with nohandler does not raise errors", () => { const wrapper = shallow( <AwesomeButton /> ); wrapper.simulate('click'); }); ✓ Clicking with no handler does not raise errors
  • 27.
  • 28.
    it("Caption is set",() => { const caption = 'test'; const wrapper = shallow( <AwesomeButton caption={caption} /> ); const captionContainer = wrapper .find('[data-awesomebutton="caption"]'); expect(captionContainer.text()).toEqual(caption); }); ✕ Caption is set (44ms) ● AwesomeButton test suite › Caption is set Method “text” is meant to be run on 1 node. 0 found instead. 16 | const captionContainer = wrapper 17 | .find('[data-awesomebutton="caption"]'); > 18 | expect(captionContainer.text()).toEqual(caption); | ^
  • 29.
    export function AwesomeButton(props:AwesomeButtonProps) { return <button onClick={props.onClick}> <span data-awesomebutton='caption'> {props.caption} </span> </button>; } ✓ Caption is set (2ms)
  • 30.
    » npm test-- --coverage PASS src/AwesomeButton.test.tsx AwesomeButton test suite ✓ Renders (5ms) ✓ Caption is set (5ms) ✓ onClick handler works (2ms) ✓ Clicking with no handler does not raise errors -------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------|----------|----------|----------|----------|-------------------| All files | 100 | 100 | 100 | 100 | | AwesomeButton.tsx | 100 | 100 | 100 | 100 | | -------------------|----------|----------|----------|----------|-------------------| Test Suites: 1 passed, 1 total Tests: 4 passed, 4 total Snapshots: 0 total Time: 3.607s
  • 31.
    Advantages ● It feelsstrangely natural ● It can up your engagement ● It’s a good way to pick up an old toolset ● It’s a great way to learn a new language ● It improves design ● It improves estimates ● It’s excellent for pair programming ● When your feature is done, so are your tests
  • 32.
    Practice A code katais an exercise in programming which helps programmers hone their skills through practice and repetition. 🔍 TDD code kata javascript
  • 33.
    Take this withyou ● Testing is as important as the feature ● Test code is as important as feature code ● TDD is about iterative programming ● TDD may make you a better programmer ● Practice ● Be pragmatic
  • 34.