liveBook · Manning

Integration test

As explained in The Art of Unit Testing 3e

Testing that verifies behavior across multiple integrated components or against external systems such as databases and networks.

An integration test checks whether code works with dependencies that are real rather than fully controlled in memory. It may use a real database, filesystem, network, REST API, system time, or configuration. The test exercises an entry point and observes a result, but it also depends on conditions outside the code being focused on. section 1.11 section 1.8

This differs from a unit test, where dependencies are in memory and their behavior is controlled. Integration tests provide useful confidence that code works with things it does not control, but that confidence costs speed, simplicity, and dependable results. A passing test checks multiple pieces working together; a failing test can leave many possible causes to investigate. section 1.11 section 10.1.3 section 1.8

Testing an asynchronous network call

Call an async function that checks a website, await its result, and assert that it reports success and an "ok" status. The test is concise, but still an integration test because the network call remains asynchronous and depends on external conditions. section 6.1.3

Expect practical trade-offs when real dependencies are involved:

  • Runs more slowly; network, database, filesystem, or thread dependencies can add substantial time. section 1.8 section 10.1.3
  • Can be flaky because timings, environments, configuration, and external services vary. section 6.1.4
  • Makes negative scenarios harder to simulate, such as unavailable networks or incorrect remote content. section 6.1.4
  • Can test irrelevant layers or dependencies, making failures longer to diagnose and easier to mistrust. section 6.1.4

Use integration tests as a complement to lower-level tests, not as a replacement for them. Keep their number limited enough to retain confidence, and separate them from fast, nonflaky unit tests. Run the fast set frequently; let an automated build pipeline run the broader feedback tests. section 6.1.4 section 7.4.3 section 10.5

Integration tests are also a practical starting point for legacy code. Before refactoring a hard-to-isolate system, capture its current behavior with tests that use the real system, change code in small steps, and run those tests often. Afterward, replace most with smaller unit tests where possible. section 12.3 section 12.4

Figure 1.7 You can have many failure points in an integration test. All the units have to work together, and each could malfunction, making it harder to find the source of a bug.
01-07

A failure can originate in any integrated unit or dependency, which explains why these tests often require broader investigation. section 1.8

How The Art of Unit Testing 3e covers it

How deep it goes
Defines real-dependency boundaries and details speed, flakiness, debugging, simulation, and legacy-refactoring trade-offs.
Examples
Demonstrates a website-status assertion and real-system XML configuration save-and-load tests.
What it assumes
Assumes JavaScript or TypeScript, ES6 features, npm, and basic Git familiarity.
What you can do after
Apply it by testing selected real dependencies, separating these tests from unit tests, and using them to protect refactoring.
How it connects
Contrasts unit tests and connects to API tests, component tests, end-to-end tests, test-driven development, and refactoring.

Also explained in

Compare how 31 books explain integration test →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Art of Unit Testing 3e. The section links point to the parts of the book it draws on.