Don’t let your
unit tests
slow you down
Daniel Irvine
@d_ir
React Advanced London 2019
@d_ir
tl;dl
@d_ir
tl;dl Good tests remove fear of change.
Confidence of change boosts your
speed.
@d_ir
tdd This is not a talk about test-driven
development!
Many TDD talks shame people into believing
everyone else is doing it wrong.
The techniques I’ll cover today will help you
even if you don’t use TDD.
@d_ir
example “TDD isn’t about unit testing!”
“You are doing it wrong if every class
has unit tests!”
What’s the actual message here?
Unit tests should still be your bread and butter.
@d_ir
10x myth Writing great tests will accelerate
your development.
Unlike working with rockstar programmers,
testing is a social activity that benefits the whole
team. Each test encodes a requirement that
everyone on the team can understand.
describe('name field', () => {

it('saves new value when form is submitted', () => {

// ...

});

});
Developer tests QA tests
@d_ir
Developer tests
• Encode requirements
• Catch regression
QA tests
@d_ir
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
@d_ir
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
@d_ir
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
@d_ir
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
• Catch performance degradation
• Check usability
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
@d_ir
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
• Catch performance degradation
• Check usability
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
Removing fear
of change
@d_ir
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
• Catch performance degradation
• Check usability
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
Removing fear
of change
@d_ir
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
• Catch performance degradation
• Check usability
Developer tests
• Encode requirements
• Catch regression
QA tests
• Encode requirements
• Catch regression
Removing fear
of change
Reducing dev
time
@d_ir
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
• Catch performance degradation
• Check usability
@d_ir
idea 1 Always follow Arrange-Act-Assert.
it('renders the customer first name', () => {

const customer = { firstName: 'Ashley' };



render(<Appointment customer={customer} />);

expect(appointmentTable().textContent).toMatch('Ashley');

});
@d_ir
idea 2 Test the lifecycle of your
components.
it('renders appointment when selected', () => {
render(

<AppointmentsDayView appointments={appointments} />

);

click(element(button));

expect(container.textContent).toMatch('Ashley');
});
@d_ir
idea 2 Test the lifecycle of your
components.
it('renders validation error when submit fails', async
() => {

render(<CustomerForm />);

await submit(form('customer'));

expect(element('.error')).not.toBeNull();
});
@d_ir
idea 2 Test the lifecycle of your
components.
it('fetches again when prop changes', async () => {
const tomorrow = new Date(today);
tomorrow.setHours(24);
await renderAndWait(<AppointmentsDay day={today} />);
await renderAndWait(<AppointmentsDay day={tomorrow} />);
expect(window.fetch).toHaveBeenLastCalledWith(
`/appointments/${tomorrow}`,
expect.anything()
);
});
@d_ir
idea 3 Be aware of each test’s surface area
R
A B
C D
D
D
@d_ir
idea 3 Be aware of each test’s surface area
R
A B
C D
D
D
Tests on

A without mocks
will exercise
functionality
in A, C and D
Test failures do not
pinpoint error
@d_ir
idea 3 Be aware of each test’s surface area
R
A B
C D
D
D
Tests on

A without mocks
will exercise
functionality
in A, C and D
Tests on B exercise
just B
Test failures do not
pinpoint error
Test failures rapidly
pinpoint error
@d_ir
idea 3 Be aware of each test’s surface area
R
A B
C D
D
D
Tests on

A without mocks
will exercise
functionality
in A, C and D
Tests on B exercise
just B
Test failures do not
pinpoint error
Test failures rapidly
pinpoint error
Tests on D exercise
just D
The usage of D in A will
still need to be tested in A
@d_ir
idea 4 Make a mess, then refactor.
(Okay, so this is kind of about TDD.)
‘Making a mess’ is a technical term that means
something like this: when you’ve got a new
requirement to implement, solve it by writing
the least amount of code in one long procedure.
Don’t think!
@d_ir
idea 5 Get out of React components at
every opportunity.
Testing components is complicated. Testing
plain objects is not. So always prefer that.
Components should be for rendering and
responding to DOM events.
Any business logic, even something as simple as
a humble if, should be elsewhere.
@d_ir
idea 6 Write your own test library.
@d_ir
idea 6 Write your own test library.
1. Replacing Enzyme or react-testing-library
with your own version is an excellent way to
further your learning.
@d_ir
idea 6 Write your own test library.
1. Replacing Enzyme or react-testing-library
with your own version is an excellent way to
further your learning.
2. These libraries lock you in to their way of
working. Rolling your own gives you a lot
more freedom to do things in other ways.
@d_ir
idea 6 Write your own test library.
1. Replacing Enzyme or react-testing-library
with your own version is an excellent way to
further your learning.
2. These libraries lock you in to their way of
working. Rolling your own gives you a lot
more freedom to do things in other ways.
3. The DOM API is straightforward. No need to
learn “yet another library”.
@d_ir
idea 6 Write your own test library.
const renderAndWait = async component =>
await act(async () =>
ReactDOM.render(component, container));
const element = selector =>
container.querySelector(selector);
const click = element =>
ReactTestUtils.Simulate.click(element);
const submit = async element =>
await act(async () =>
ReactTestUtils.Simulate.submit(element)
);
@d_ir
idea 6 Write your own test library.
click(elementMatching(id('addCustomer')));
It works for shallow rendering too…
const buttons = childrenOf(
elementMatching(className('button-bar'))
);
expect(elementMatching(type(CustomerForm))
.toBeDefined();
@d_ir
Question: How are you going to learn testing?
Most effective
Least effective
Pair and mob with experienced developers
Read TDD books and apply what you learn
Read well-tested codebases
Attend coding dojos
Work on TDD katas
@d_ir
@d_ir
@d_ir
@d_ir
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
Snapshots are a useful way to quickly check if
your component’s visual state has changed
unexpectedly. 



You can visually inspect the HTML output of your
component.
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
• Encode requirements
• Catch regression
• Assist with design
• Guide refactoring
• Explain your code to others
• Pinpoint errors
• Minimize debugging
• Minimize manual testing
🤔
🤔
@d_ir
Question: How well do snapshots fit our
definition of developer tests?
Hinderance Help
Bad tests Great tests
No tests
@d_ir
Finish Tests can overcome your fear of
change and help you work faster.
What about the fear of getting started?
Yes, you will write bad tests when you start. You
just have to keep going. Keep practicing. You’ll
improve.
@d_ir
Finish Tests can overcome your fear of
change and help you work faster.
What about the fear of getting started?
Yes, you will write bad tests when you start. You
just have to keep going. Keep practicing. You’ll
improve.
Thank you
Daniel Irvine

Don't let your tests slow you down

  • 1.
    Don’t let your unittests slow you down Daniel Irvine @d_ir React Advanced London 2019
  • 2.
  • 3.
    @d_ir tl;dl Good testsremove fear of change. Confidence of change boosts your speed.
  • 4.
    @d_ir tdd This isnot a talk about test-driven development! Many TDD talks shame people into believing everyone else is doing it wrong. The techniques I’ll cover today will help you even if you don’t use TDD.
  • 5.
    @d_ir example “TDD isn’tabout unit testing!” “You are doing it wrong if every class has unit tests!” What’s the actual message here? Unit tests should still be your bread and butter.
  • 6.
    @d_ir 10x myth Writinggreat tests will accelerate your development. Unlike working with rockstar programmers, testing is a social activity that benefits the whole team. Each test encodes a requirement that everyone on the team can understand. describe('name field', () => {
 it('saves new value when form is submitted', () => {
 // ...
 });
 });
  • 7.
    Developer tests QAtests @d_ir
  • 8.
    Developer tests • Encoderequirements • Catch regression QA tests @d_ir
  • 9.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression @d_ir
  • 10.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression @d_ir • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing
  • 11.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression @d_ir • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing • Catch performance degradation • Check usability
  • 12.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression @d_ir • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing • Catch performance degradation • Check usability
  • 13.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression Removing fear of change @d_ir • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing • Catch performance degradation • Check usability
  • 14.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression Removing fear of change @d_ir • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing • Catch performance degradation • Check usability
  • 15.
    Developer tests • Encoderequirements • Catch regression QA tests • Encode requirements • Catch regression Removing fear of change Reducing dev time @d_ir • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing • Catch performance degradation • Check usability
  • 16.
    @d_ir idea 1 Alwaysfollow Arrange-Act-Assert. it('renders the customer first name', () => {
 const customer = { firstName: 'Ashley' };
 
 render(<Appointment customer={customer} />);
 expect(appointmentTable().textContent).toMatch('Ashley');
 });
  • 17.
    @d_ir idea 2 Testthe lifecycle of your components. it('renders appointment when selected', () => { render(
 <AppointmentsDayView appointments={appointments} />
 );
 click(element(button));
 expect(container.textContent).toMatch('Ashley'); });
  • 18.
    @d_ir idea 2 Testthe lifecycle of your components. it('renders validation error when submit fails', async () => {
 render(<CustomerForm />);
 await submit(form('customer'));
 expect(element('.error')).not.toBeNull(); });
  • 19.
    @d_ir idea 2 Testthe lifecycle of your components. it('fetches again when prop changes', async () => { const tomorrow = new Date(today); tomorrow.setHours(24); await renderAndWait(<AppointmentsDay day={today} />); await renderAndWait(<AppointmentsDay day={tomorrow} />); expect(window.fetch).toHaveBeenLastCalledWith( `/appointments/${tomorrow}`, expect.anything() ); });
  • 20.
    @d_ir idea 3 Beaware of each test’s surface area R A B C D D D
  • 21.
    @d_ir idea 3 Beaware of each test’s surface area R A B C D D D Tests on
 A without mocks will exercise functionality in A, C and D Test failures do not pinpoint error
  • 22.
    @d_ir idea 3 Beaware of each test’s surface area R A B C D D D Tests on
 A without mocks will exercise functionality in A, C and D Tests on B exercise just B Test failures do not pinpoint error Test failures rapidly pinpoint error
  • 23.
    @d_ir idea 3 Beaware of each test’s surface area R A B C D D D Tests on
 A without mocks will exercise functionality in A, C and D Tests on B exercise just B Test failures do not pinpoint error Test failures rapidly pinpoint error Tests on D exercise just D The usage of D in A will still need to be tested in A
  • 24.
    @d_ir idea 4 Makea mess, then refactor. (Okay, so this is kind of about TDD.) ‘Making a mess’ is a technical term that means something like this: when you’ve got a new requirement to implement, solve it by writing the least amount of code in one long procedure. Don’t think!
  • 25.
    @d_ir idea 5 Getout of React components at every opportunity. Testing components is complicated. Testing plain objects is not. So always prefer that. Components should be for rendering and responding to DOM events. Any business logic, even something as simple as a humble if, should be elsewhere.
  • 26.
    @d_ir idea 6 Writeyour own test library.
  • 27.
    @d_ir idea 6 Writeyour own test library. 1. Replacing Enzyme or react-testing-library with your own version is an excellent way to further your learning.
  • 28.
    @d_ir idea 6 Writeyour own test library. 1. Replacing Enzyme or react-testing-library with your own version is an excellent way to further your learning. 2. These libraries lock you in to their way of working. Rolling your own gives you a lot more freedom to do things in other ways.
  • 29.
    @d_ir idea 6 Writeyour own test library. 1. Replacing Enzyme or react-testing-library with your own version is an excellent way to further your learning. 2. These libraries lock you in to their way of working. Rolling your own gives you a lot more freedom to do things in other ways. 3. The DOM API is straightforward. No need to learn “yet another library”.
  • 30.
    @d_ir idea 6 Writeyour own test library. const renderAndWait = async component => await act(async () => ReactDOM.render(component, container)); const element = selector => container.querySelector(selector); const click = element => ReactTestUtils.Simulate.click(element); const submit = async element => await act(async () => ReactTestUtils.Simulate.submit(element) );
  • 31.
    @d_ir idea 6 Writeyour own test library. click(elementMatching(id('addCustomer'))); It works for shallow rendering too… const buttons = childrenOf( elementMatching(className('button-bar')) ); expect(elementMatching(type(CustomerForm)) .toBeDefined();
  • 32.
    @d_ir Question: How areyou going to learn testing? Most effective Least effective Pair and mob with experienced developers Read TDD books and apply what you learn Read well-tested codebases Attend coding dojos Work on TDD katas
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? Snapshots are a useful way to quickly check if your component’s visual state has changed unexpectedly. 
 
 You can visually inspect the HTML output of your component.
  • 38.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing
  • 39.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing
  • 40.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔
  • 41.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔
  • 42.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔 🤔
  • 43.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔 🤔
  • 44.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔 🤔
  • 45.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔 🤔
  • 46.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? • Encode requirements • Catch regression • Assist with design • Guide refactoring • Explain your code to others • Pinpoint errors • Minimize debugging • Minimize manual testing 🤔 🤔
  • 47.
    @d_ir Question: How welldo snapshots fit our definition of developer tests? Hinderance Help Bad tests Great tests No tests
  • 48.
    @d_ir Finish Tests canovercome your fear of change and help you work faster. What about the fear of getting started? Yes, you will write bad tests when you start. You just have to keep going. Keep practicing. You’ll improve.
  • 49.
    @d_ir Finish Tests canovercome your fear of change and help you work faster. What about the fear of getting started? Yes, you will write bad tests when you start. You just have to keep going. Keep practicing. You’ll improve. Thank you Daniel Irvine