Unit Testing React

Unit Testing Basics

  • Unit tests directly call functions in application code.
    • No browser client, no web server, as with Cypress
  • Test runners like Vitest and Jest collect, run, and report on which tests pass.
  • Test libraries like Chai provide functions to make it easier to write readable test code.
  • Libraries like the React Testing Library add functions for testing React applications.

End to End (E2E) Tests

  • Tests simulate a user interacting with a web server.
    • A server must be running, locally or publicly.
    • Cypress: JavaScript-based, most commonly used with modern web apps like React
    • Selenium WebDriver: can be run in Java, Ruby, Python, ...
  • Tester sends actions to an internal browser and analyzes the HTML returned.
  • Application is a black box. Tester has no access to the application code.

Unit vs E2E Tests

Unit TestsE2E Tests
What they interact with application code, e.g., a test will call a component function and inspect the HTML returned application user interface, e.g., a test will open a URL and click on a button
Expected time to run microseconds seconds or more
Isolating from databases and services Mock objects Emulators, testbed databases, mock service workers

Common rules about tests

  • Tests are independent, may run in any order or even in parallel
  • Repeated setup actions, e.g., creating standard sample data, go in a setup method
  • Repeated cleanup actions go in a tear down methods

Common Formats

  • Behavior-Driven (Vitest, Jest, ...)
    • "it should ..." sentences describe desired behavior being tested
    • expect() calls test for results
  • Older object-oriented style (JUnit, Nunit, ...):
    • tests inherit assert methods from a TestCase class
    • utility classes find and run all instances of tests and report results

Describing Tests

  • Label tests with descriptive texts rather than function names
describe('sorting the list of users', function() {
  it('sorts in descending order by default', function() {
    ...
  });
});

Expected Values

  • Matcher methods on expect() describe expected results
expect(isLeapYear(2000)).toBe(true);

Unit Tests: The Challenge

  • Unit tests
    • should be numerous, fast, automated
    • should test only the unit, not other classes
  • How can you unit test code that
    • calls code in other modules
    • is slow, e.g., opens a database connections
    • calls code that doesn't exist yet (test-driven development)

Solution: Mock Objects

  • Mocks let you test that code calls another object correctly and uses the results correctly
    • A mock is a spy that records calls to it
    • A mock is a stub that imitates an object's responses
  • Mock libraries provide tools for making mocks in just a few steps
  • Mocks are also very useful for "Plan B" demos!!

Mocking resources

My React unit testing advice

What to Test

  • Every interaction needed by any demo path
  • Every case of possible invalid user input
  • User input sequences that should (or should not) reset input data

What Not to Test

  • Private functions
  • Trivial functions like setters and getters
  • Logging functions like toString

Readings: E2E vs Unit testing

Readings: More on Mock Objects

Thanks to Hakim El Hattab for RevealJS