Day 2, Session 1

Testing!

Components

What do we test?

  • Given a set of initial states, does the component exhibit the appropriate side-effects/behavior

    • Should be minimal, and separated from complex hook behavior

  • Given a set of initial states, does the component render the appropriate output

    • Limited exclusively to the code logic in the associated component file

Why?

  • Components are our View part of the UI. the tests of these component should solely focus on making sure that they present the right views in the appropriate circumstances

How?

  • Separate complex hook logic into custom hook, and mock that hook when testing the component

  • Mock all sub-components

  • Take snapshots of the component in different states (incoming props and hook output), and inspect those snapshots for relevant details.

Hooks

What do we test?

  • Given a set of initial states, does the hook exhibit the appropriate side-effects/behavior

  • Given a set of initial states, does the hook return the appropriate output

    • Limited exclusively to the code logic in the associated component file

Why?

  • Hooks are our interaction and glue step of our UI. They gather data from the model and communicate it to components, as well as configuring their own behaviors that can be associated with those components. Testing these behaviors in isolation allows for thorough examination of the behaviors without having to mix it with simulating component states.

How?

  • Mock all react hooks to have testable output

  • use keyed state (from react-unit-test-utils) to validate state usage.

Asynchronous events

What do we test?

  • Given a successful return of valid data, does the expected follow-on behavior occur

  • Given a failure of the asynchronous event, does the expected follow-on behavior occur

Why?

  • For network/async events, we want to be able to cleanly simulate success and failure without relying on the external resource that the request would be communicating with.

How?

  • Mock functions that return Promises to return Promise.resolve(validData) or Promise.reject(error)

  • If necessary for complex systems, you can instead return custom promises that export their resolve/reject logic to be controlled externally

    • const promiseCallbacks = {}; const myPromise = new Promise((resolve, reject) => { promiseCallbacks.resolve = resolve; promiseCallbacks.reject = reject; })