Failure & Recovery

The Course
•
4 min read

Make each state observable

Open the reading queue lab. Run it fresh, search web, and expect one result. Search a term with no matches and expect zero results without an error. Save a book before testing failures so you can verify saved data is preserved.

Choose Service error and submit: feedback should report the failure and Results should be empty. Switch to Success and resubmit: the error should clear. Choose Malformed data: the decoder must reject the response instead of displaying 42 as a title. The scenario control affects a new attempt, not one already started.

Choose Slow, search web, then immediately choose Fast and search testing. The final result must be Testing Useful Programs. Repeat the sequence with a slow failing attempt followed by fast success. An older failure must not erase the newer result or message. Cancel a slow attempt and confirm it stays idle after the delay would have elapsed.

Use Tab and Enter to submit and save/remove. Feedback uses role=status; the result region exposes aria-busy. The app returns focus to the query after a row action because rebuilding the list removes the old button. Manual keyboard checks complement controller tests; a pure test cannot prove a person can reach the buttons.

This is a contract matrix: successful empty data, transport-style error, malformed success, recovery, stale work, intentional cancellation, and interaction. Choose expected outcomes first, then compare. Do not call an app reliable merely because the first search worked.

Predict and trace

Write the output before running. Follow the values through each call; if your prediction differs, identify the first step that surprised you.

Predict, then run JavaScript
const cases = [
  { status: "ready", count: 0, error: "" },
  { status: "error", count: 0, error: "Service unavailable" }
];

for (const item of cases) {
  console.log(item.status === "error" ? item.error : `${item.count} results`);
}
Check your prediction
0 results
Service unavailable

A successful empty result and a failure need different messages even though both lists have zero entries.

Compare failure and recovery through the interface

Start with a successful search, trigger one failure, then recover with another valid search. Record status, results and saved choices separately: the right error message does not prove unrelated data survived. Use the controlled tests for stale completion rather than guessing delays in the interface. Attach the old request’s handlers before rejecting its promise so the test does not create an unrelated unhandled rejection.

Boundary or caseWhat to inspect
Service rejectionError state explains failure without claiming empty success
Successful empty listReady-empty is different from the initial prompt
Failure then recoveryNew success clears the old error and preserves saved choices

Locate corresponding model, controller and interface checks. Change one service behavior at a time, predict the resulting state, and restore it before another experiment. Explain which test would detect a stale error overwriting newer success.

Build it yourself

Exercise

Failure & Recovery: practise

Write describe(state) distinguishing idle, loading, error, and ready. Print an empty success and a failure; do not turn failure into a zero-result success.

Your working space JavaScript
// State shape matches the reading queue.
Hint 1: the idea

Check status before formatting results.length.

Hint 2: a first step

An error branch should return its error message.

Show one solution
function describe(state) {
  if (state.status === "idle") {
    return "Start a search";
  }

  if (state.status === "loading") {
    return "Searching";
  }

  if (state.status === "error") {
    return state.error;
  }

  return `${state.results.length} results`;
}

console.log(describe({ status: "ready", results: [] }));
console.log(describe({ status: "error", error: "Try again", results: [] }));
0 results
Try again

Write down the exact steps and observed result of one manual failure case. Then point to the automated test that covers the controller part of that behavior.

Test a changed requirement

Write down the exact steps and observed result of one manual failure case. Then point to the automated test that covers the controller part of that behavior.

Explain before moving on

Which claims require browser testing in addition to model tests? What is the expected result of cancelling a slow failure?

Was this article helpful?

Your response is saved on this device.