51
Mehdi Valikhani Software Engineer at AutopilotHQ 06 / 02 / 2017 | React Sydney Meetup TDD: Develop, refactor and release with confidence

TDD: Develop, Refactor and Release With Confidence

Embed Size (px)

Citation preview

Page 1: TDD: Develop, Refactor and Release With Confidence

Mehdi Valikhani Software Engineer at AutopilotHQ

06 / 02 / 2017 | React Sydney Meetup

TDD: Develop, refactor and release with

confidence

Page 2: TDD: Develop, Refactor and Release With Confidence

“Worried that TDD will slow down your

programmers? Don't. They

probably need slowing down.”

— J. B. Rainsberger

Page 3: TDD: Develop, Refactor and Release With Confidence

Writing Testable Code

Page 4: TDD: Develop, Refactor and Release With Confidence

I. Avoiding big and multi-purpose

units of code (Functions, Classes, Components)

Page 5: TDD: Develop, Refactor and Release With Confidence

II. Developing small and focused units of code

Page 6: TDD: Develop, Refactor and Release With Confidence

III. Separation of concerns

Page 7: TDD: Develop, Refactor and Release With Confidence

IV. Using global objects

Page 8: TDD: Develop, Refactor and Release With Confidence

CAUTION: OVER-ENGINEERING TRAP AHEAD

Be careful when breaking down big components to multiple small ones.

8Writing Testable Code: Caution

Page 9: TDD: Develop, Refactor and Release With Confidence

TDD Principles

Page 10: TDD: Develop, Refactor and Release With Confidence

I. Don’t test behaviour

of dependencies (Both internal and external)

Page 11: TDD: Develop, Refactor and Release With Confidence

II. Don’t integrate with internals

of a unit of code (Components, Functions, Classes)

Page 12: TDD: Develop, Refactor and Release With Confidence

III. Tests should be

execution-order agnostic

Page 13: TDD: Develop, Refactor and Release With Confidence

IV. Tests should be

independent

Page 14: TDD: Develop, Refactor and Release With Confidence

V. Tests should not have

side effects

Page 15: TDD: Develop, Refactor and Release With Confidence

VI. Feedback

Page 16: TDD: Develop, Refactor and Release With Confidence

VII. Focused Tests

Page 17: TDD: Develop, Refactor and Release With Confidence

VIII. Consistency

Page 18: TDD: Develop, Refactor and Release With Confidence

IX. Don’t be DRY

Page 19: TDD: Develop, Refactor and Release With Confidence

X. Iterate, Review,

Improve

Page 20: TDD: Develop, Refactor and Release With Confidence

XI. Test Code is Untested

Page 21: TDD: Develop, Refactor and Release With Confidence

Unit Tests

Page 22: TDD: Develop, Refactor and Release With Confidence

I.  Component Internals

Don’t test internals of a component

Page 23: TDD: Develop, Refactor and Release With Confidence

— Example <UserProfile /> component

class UserProfile extends React.Component {

    _pathToAvatar(userId) {

        return `my-cdn.com/users/${userId}/avatar-320.png`;

    }

    render() {

        return (

            <div className=“user”>

                <h3 className=“user__name”>{this.props.name}</h3>

                <img 

                    className=“user__avatar” 

                    src=`${this._pathToAvatar(this.props.id)}` 

                />

           </div>

        );

    }

}

23Unit Tests: Component Internals

Page 24: TDD: Develop, Refactor and Release With Confidence

— Bad Test

it(‘_pathToAvatar() returns correct path for given user id’, () => {

    const userProfile = shallow(<UserProfile name=“Taco” id=“123” />);

    const subject = userProfile.instance()._pathToAvatar;

    const result = subject(123);

    expect(result).to.equal('my-cdn.com/users/123/avatar-320.png');

});

24Unit Tests: Component Internals

Page 25: TDD: Develop, Refactor and Release With Confidence

— Bad Test

it(‘_pathToAvatar() returns correct path for given user id’, () => {

    const userProfile = shallow(<UserProfile name=“Taco” id=“123” />);

    const subject = userProfile.instance()._pathToAvatar;

    const result = subject(123);

    expect(result).to.equal('my-cdn.com/users/123/avatar-320.png');

});

— Good Test

it(‘rendered image points to correct asset’, () => {

    const userProfile = shallow(<UserProfile name=“Taco” id=“123” />);

    const subject = userProfile.find(‘.user__avatar’);

    const result = subject.prop(’src');

    expect(result).to.equal('my-cdn.com/users/123/avatar-320.png');

});

25Unit Tests: Component Internals

Page 26: TDD: Develop, Refactor and Release With Confidence

II.  Limit Your Implementation

Knowledge

Page 27: TDD: Develop, Refactor and Release With Confidence

— Example <Avatar /> component

const Avatar = ({ className, path }) => {

    const classes = classNames(‘avatar’, className);

    return (

        <img src={path} className={classes} />

    );

}

27Unit Tests:  Limit Your Implementation Knowledge

Page 28: TDD: Develop, Refactor and Release With Confidence

— Example <UserProfile /> component

class UserProfile extends React.Component {

    _pathToAvatar(userId) {

        return `my-cdn.com/users/${userId}/avatar-320.png`;

    }

    render() {

        return (

            <div className=“user”>

                <h3 className=“user__name”>{this.props.name}</h3>

                <Avatar 

                    className=“user__avatar” 

                    path=`${this._pathToAvatar(this.props.id)}` 

                />

           </div>

        );

    }

}

28Unit Tests:  Limit Your Implementation Knowledge

Page 29: TDD: Develop, Refactor and Release With Confidence

— Bad Test

it(‘rendered image points to correct asset’, () => {

    const userProfile = shallow(<UserProfile name=“Taco” id=“123” />);

    const subject = userProfile.find(‘.icon’);

    const result = subject.prop(’src');

    expect(result).to.equal('my-cdn.com/users/123/avatar-320.png');

});

29Unit Tests:  Limit Your Implementation Knowledge

Page 30: TDD: Develop, Refactor and Release With Confidence

— Bad Test

it(‘rendered image points to correct asset’, () => {

    const userProfile = shallow(<UserProfile name=“Taco” id=“123” />);

    const subject = userProfile.find(‘.icon’);

    const result = subject.prop(’src');

    expect(result).to.equal('my-cdn.com/users/123/avatar-320.png');

});

— Good Test

it(‘rendered image points to correct asset’, () => {

    const userProfile = shallow(<UserProfile name=“Taco” id=“123” />);

    const subject = userProfile.find(Avatar);

    const result = subject.prop(’path');

    expect(result).to.equal('my-cdn.com/users/123/avatar-320.png');

});

30Unit Tests:  Limit Your Implementation Knowledge

Page 31: TDD: Develop, Refactor and Release With Confidence

III. Test Structure

Page 32: TDD: Develop, Refactor and Release With Confidence

IV. Host-agnostic Tests

Page 33: TDD: Develop, Refactor and Release With Confidence

TOOLS

enzyme chai

mocha

33Unit Tests: Tools

Page 34: TDD: Develop, Refactor and Release With Confidence

Snapshot Tests

Page 35: TDD: Develop, Refactor and Release With Confidence

Problems with snapshot testing

35Snapshot Tests

1. Validation of the “gold master” snapshot is manual and should be done by developer

2. Unlike unit tests, snapshot tests don’t provide specific guidance for what the original developer expected beyond the “actual behaviour”

3. They’re often fragile and generate lots of false negatives

Page 36: TDD: Develop, Refactor and Release With Confidence

Best Practices

36Snapshot Tests

1. Well-formatted snapshots

2. Proper diff logs

3. Skip composed components

Page 37: TDD: Develop, Refactor and Release With Confidence

— Example

import React from 'react';

import { shallow } from 'enzyme';

import { expect } from 'chai';

import generateSubtree from 'enzyme-to-json';

import Button from './button';

describe(‘<Button />’, () => {

    it(‘Generated markup matches golden master’, function test() {

        const subject = shallow(

            <Button size=“small” color=“blue” icon=“save” text=“Save" />

        );

        const result = generateSubtree(subject);

        expect(result).to.matchSnapshot(

`${this.test.file}.snap`, 

this.test.title);

    });

});

37Snapshot Tests

Page 38: TDD: Develop, Refactor and Release With Confidence

TOOLS

enzyme-to-json chai-jest-snapshot

38Snapshot Tests: Tools

Page 39: TDD: Develop, Refactor and Release With Confidence

Visual Regression

Tests

Page 40: TDD: Develop, Refactor and Release With Confidence

I. Test “widgets”, not

individual components

Page 41: TDD: Develop, Refactor and Release With Confidence

II. Do not test multiple “widgets” together

Page 42: TDD: Develop, Refactor and Release With Confidence

III. Mock Data

Page 43: TDD: Develop, Refactor and Release With Confidence

IV. Comparison Tool

Page 44: TDD: Develop, Refactor and Release With Confidence

TOOLS

webdriver.io wdio-visual-regression-service

44Visual Regression Tests: Comparison Tool: Tests

Page 45: TDD: Develop, Refactor and Release With Confidence

Tips

Page 46: TDD: Develop, Refactor and Release With Confidence

I. Well-structured Bug Backlog

Page 47: TDD: Develop, Refactor and Release With Confidence

Bug Backlog Insights

47Tips: Well-structured Bug Backlog

1. Identify missing tests

2. Improve code-review processes

3. Improve task spec

4. Training sessions

5. Pair programming

Page 48: TDD: Develop, Refactor and Release With Confidence

II. Opportunities from

Fixing Bugs

Page 49: TDD: Develop, Refactor and Release With Confidence

Opportunities from Fixing Bugs

49Tips: Opportunities from Fixing Bugs

1. Re-evaluate code readability

2. Investigate source of bug

3. Re-evaluate test readability

4. Re-evaluate existing test’s behaviour

Page 50: TDD: Develop, Refactor and Release With Confidence

Questions?

Page 51: TDD: Develop, Refactor and Release With Confidence

Thank You!

You can reach me at:

[email protected]

" @mehdivk