Testing... Testing...
1, 2, NaN

A voyage into testing that fickle devil known as JavaScript... and beyond!

Gant

Laborde.com

[ graphic ]

maybe something about preparedness here

readability etc.

Caveats:

  • Infomercials

  • Imperfect Coverage

 

Warning!!!

Why?  Because...

Imperfect Coverage

My perspective.

Sooo... lots of React Native

How to use this talk?

Why Tests?

?

Why Tests?

but seriously...

Tests Pass

Tests...

How do they work?

Smart People

Teams

who want to ship durable code

But I'm not a testing expert!

The Tester's Crisis

😱

Testing Myths

aka Excuses

🦄 Tests make projects harder

🦄 Backfilling tests is a good or bad thing.

RED - GREEN - REFACTOR

🦄 Thinking tests before code is too hard.

Think Functional

  • One input → one output
  • Declarative code
  • Parallel friendly
  • Lazy evaluation

If you're doing the same thing in two places, DRY your code."

- Everyone

 

 

 

If you're doing two things in one place, split your code."

- Gant (and probably everyone, too)

🦄 Testing is algorithmic and not heuristic.

🦄 Testing is pass/fail with no between.

Does that mean in the end I need to roll my own tests suite?

... yes, but...

YES

Engineering Objective

"What features are important for an effective foundation of this product?"

Testing Objective

"What tests are important for an effective foundation of this product?"

The Testing Domain

The Test Pyramid

"-ish"

The Test Pyramid

"-ish"

The Test Pyramid

"-ish"

Venture with me!

Unit Tests

simple, cheap, tests

Code

Do some thing!

Test

Did it do the thing?

Unit Tests

But be functional!!!!

JEST

Fan of AVA?  Try this article

https://bit.ly/2PY3O4b

Unit Tests

import React from "react"
import { MagicButton } from "../src/components/magicButton"
import renderer from "react-test-renderer"

test("MagicButton has onPress fn", () => {
  let x = 0
  const buttonInstance = renderer
    .create(<MagicButton onPress={() => x++} />)
    .getInstance()
  expect(buttonInstance.handleCrazyPress).toBeDefined()
  expect(x).toBe(0)
  buttonInstance.props.handleCrazyPress()
  expect(x).toBe(1)
})

Simple!  👍

React Testing Library

function setAnswer(question, answer) {
  fireEvent.changeText(question, answer);
}

test('should verify two questions', () => {
  const { getAllByType, getByText } = render(<QuestionsBoard {...props} />);
  const allQuestions = getAllByType(Question);

  setAnswer(allQuestions[0], 'a1');
  setAnswer(allQuestions[1], 'a2');

  fireEvent.press(getByText('submit'));

  expect(props.verifyQuestions).toBeCalledWith({
    '1': { q: 'q1', a: 'a1' },
    '2': { q: 'q2', a: 'a2' },
  });
});

Simpler!  👍

GOTCHAS!

Mock it All

Mock API


  // setupJest.js or similar file
  global.fetch = require('jest-fetch-mock')

jest-fetch-mock

custom mock


  const API = require('mockAPI')

JEST automatically uses the mock folder

MockDate


  describe("My Date() Tests", () => {
    beforeEach(() => {
      MockDate.set("2012-12-12T00:00:00.000Z")
    })

    afterEach(() => {
      MockDate.reset()
    })

    ...

  })

DOUBLE GOTCHA

Make sure mocking is deterministic

import React from 'react'
import Mole from '../../src/views/Mole/mole'
import renderer from 'react-test-renderer'

test('Inactive Mole Press', () => {
  const moleInstance = renderer.create(<Mole />).getInstance()
  moleInstance.handlePressIn()
  expect(moleInstance.state.currentImage.testUri).toContain('hole')
})

Passes on Mac - Fails on Windows

Unit Testing

 

 

Ideally:  Where we keep declarative code accountable.

Integration

Integration

Integration

Not so much in React

Integration

Forcing integration can be false security.

UNLESS

  • You violated or blurred encapsulation
  • You are checking performance
  • You are checking visuals

Integration

Integration

Snapshot Unit Tests

⏩ 📸 🎉

FAST!!!!

Snapshot Unit Tests

This replaces:

  • console.log the object
  • copy and paste it into the test
  • then check for equality

Snapshot Unit Tests

const temp = createAmazingObject('test', 'φ', 1.61803)
expect(temp).toMatchSnapshot()

feel free to use snapshots on anything that's not data.

Snapshot Unit Tests

React components are PERFECT for snapshot tests!

Snapshot Storybook

Because Storybook Rocks!

Snapshot Storybook


import initStoryshots from "@storybook/addon-storyshots"

initStoryshots({
  configPath: "./storybook",
  framework: "react-native",
})

Now all storybook stories are snapshot tests

Snapshot State

Automatic Tests!?


  import { testStateMachine } from 'react-automata'
  import { App } from '../App'

  test('all state snapshots', () => {
    testStateMachine(App, { fixtures })
  })

Assure your app code handles all combinations of state you've identified!

Other automatic examples?

Dependency injected middleware combinations

E2E Tests

Gray vs Black

Blackbox Sounds Great

sleep(4)
sleep(9)
sleep(12)
sleep(17)

Cypress

React.js Testing Awesome

Cypress

Try it yourself!  Especially the time travel.

Recordings

Cypress for Web 👍

Mobile? 🤷‍♀️

Appium

http://appium.io/

Detox

"rehab": "detox build && detox test"

GOTCHAS!

Setup

  • package.json Edits

  • config.json Edits

 

 

Set time for setup!

Detox


describe("Example", () => {
  beforeEach(async () => {
    // await device.reloadReactNative()
    await device.launchApp({ newInstance: true })
  })

  it("should have packing list screen", async () => {
    await expect(element(by.id("packingListScreen"))).toBeVisible()
  })

  it("should have an input screen", async () => {
    await element(by.text("Input")).tap()
    await expect(element(by.id("listInputScreen"))).toBeVisible()
  })
})

Detox

Given that accessibilityLabel is an outwardly-facing string that is actually used by accessibility screen readers (and should be localized to the device user's language), Apple now provides an alternate property (iOS 5+) that is specifically intended for UI Automation purposes"

Detox

Detox

  describe("List Input Test", () => {
    beforeEach(async () => {
      // await device.reloadReactNative()
      await device.launchApp({ newInstance: true })
      await element(by.text("Input")).tap()
    })

    it("should have list input screen", async () => {
      await expect(element(by.id("listInputScreen"))).toBeVisible()
    })

    it("should have a list input component", async () => {
      await expect(element(by.id("inputField"))).toBeVisible()
      await element(by.id("inputField")).replaceText("Socks")
      await element(by.text("Add item to List")).tap()
      await element(by.text("List")).tap()
      await expect(element(by.text("Socks"))).toBeVisible()
    })
  })

Detox

Detox

Strong Coupling to React Native

When you upgrade React Native, you can break Detox tests.

 

When upgrade Detox, you can break Detox tests.

Detox Instruments?

Soft-Tests

linters++

Style Linting

Prettier Rocks

Complexity Audits

http://jshint.com

Company Audits

Accessibility?

GOTCHAS!

Common Testing Stack

When

to test on open codebases?

  • On Commit?
  • On Push?
  • On PR?

Rely on CI!

Soft CI?

Danger CI 🚫

"Stop saying 'You forgot to ...' in code review"

 

Write JavaScript!

Beyond!

A New View

Environment Tests?

wat?

API Snapshots?

protecting REST API Calls Backwards

API Snapshots?

Enforce API structure with Schemas

API Snapshots

Perf Testing

Not "How" but "How Well"

Perf Testing

https://benchmarkjs.com/

 

https://www.webpagetest.org/

https://developers.google.com/speed/pagespeed/insights/

 

Performance API of your particular browser

(performance.mark / performance.measure)

Perf Testing

Copy-Paste Testing?

DRY code is maintainable code

Size - Audit

  • Images

  • App Data

  • Dependencies

Test Tests?

use https://codecov.io/ or others

Coverage Reporting

Test Dependencies?

Non-trivial Updating

Test Dependencies?

greenkeeper.io

Test Dependencies?

updtr

Test Dependencies?

Unused!?  try npm-check

Madge Graphs

Madge Circular Checks

$ yarn madge --circular path/src/






$ yarn madge --extensions ts --circular src/

Anomaly Detection?

Static Type Checking

PLEASE!

Chaos Testing

Chaos Testing

Localization

Generalized Chaos

 

By breaking systems on purpose with chaos engineering, engineers can find vulnerabilities and address them before they result in downtime and lost revenue.

Generalized Chaos

Naughty Code

Parameterized Chaos

TestCheck.js

works with most common testing libs

check(
  property(
    gen.int, gen.int,
    (a, b) => a + b >= a && a + b >= b
  )
)

TestCheck.js

Simplest failcase

{ result: false,
  failingSize: 2,
  numTests: 3,
  fail: [ 2, -1 ],
  shrunk:
   { totalNodesVisited: 2,
     depth: 1,
     result: false,
     smallest: [ 0, -1 ] 
   } 
}

Parameterized Chaos

Parameterized Chaos

 

 

 

 

 

Typescript version?

Manual Testing

QA and UATs

Manual Testing

With React Consider

All States

  • Failure State
  • Invalid State
  • Offline State
  • No Permission State
  • Localizations
  • Return App State

With React Consider

  • Devices/Platforms
  • Orientation
  • 3rd Party Failure
  • Low light environment
  • Simple gestures
  • Button mashing
  • Screen Readers

And all combinations!!!

Production Testing

Crowd-sourced Errors

Production Testing

You never know when a crash is gonna happen

Production Testing

Production Testing

React

React Native

Production Testing

A/B Testing?

Testing Pains!

Not JS

beyond the beyond

Non-JS Testing

README Testing

Just trying to be ridiculous now...

Find the ones that fit your project, and use them!

Accessibility - Audit

Lighthouse

 

AND SO MANY MORE:

https://www.w3.org/WAI/ER/tools/

JSX - Audit

How is Everything Connected?

Great if you show up and there ARE NO TESTS!!!

jsx-info

What SHOULD

your tests look like?

Consider a testing matrix

Bitesized Feedback

The question is:
"What needs to be durable and effective for your software?"

And sometimes that answer changes!

It can feel messy

It can feel crazy

But who do we blame?

Good Tools and Great Wisdom

Thanks

DevNexus

GantLaborde.com

@GantLaborde

Testing... Testing... 1, 2, NaN

By Gant Laborde

Testing... Testing... 1, 2, NaN

Testing talk for ForwardJS

  • 526
Loading comments...

More from Gant Laborde