Unit testing vs integration testing

tldr: Unit tests check one function in isolation with dependencies mocked. Integration tests check that real components work together. Units are fast and cheap, so you write many. Integration tests are slower but catch the wiring bugs mocks hide. A healthy suite needs both, in that order of quantity.


The difference, side by side

Unit testingIntegration testing
ScopeOne function, method, or classTwo or more real components
DependenciesMocked or stubbedReal: databases, APIs, queues
TypeTypically white-box: you know the codeOften black-box at the interface: you know the contract
SpeedMillisecondsSeconds
Typical countHundreds to thousandsDozens to hundreds
Written byThe developer, with the codeDevelopers, sometimes QA
A failure meansThis logic is wrongThese pieces do not connect correctly

Two notes the classic comparison tables get wrong. The white-box and black-box labels are typical, not a law: sociable unit tests exercise real collaborators, and plenty of integration tests inspect internals. And developers write both. Integration tests are not a hand-off to a later QA phase; they live in the same repository and run in the same CI as the unit tests.

What unit tests catch

A unit test pins down logic. Given this input, the function returns that output. Discount math, date parsing, permission checks, edge cases in a validator. Because everything around the unit is mocked, a failure has exactly one suspect, and a full run finishes fast enough to execute on every save.

The mocking is the strength and the blind spot at once. The test passes as long as the code honors the contract the mock encodes. Nothing checks that the contract matches reality.

A unit test sees one function with mocked dependencies; an integration test sees real components talking across a seam

What integration tests catch

Integration tests exist for the gap mocks leave. The classics:

  • The ORM generates a query the real database rejects
  • Service A sends userId, service B expects user_id
  • An API changed its error format and the mock never heard
  • Transactions, timeouts, and connection pools behaving unlike any stub

Each of these passes unit testing everywhere and fails the moment two real components meet. The middle layer of the testing pyramid exists for exactly this class of bug.

A concrete failure each level misses

A checkout service computes tax and sends the payment provider an amount in dollars, but the provider expects cents. And in the reverse direction: a rounding bug charges 9.98 instead of 9.99 on one specific basket.

BugWhat unit tests reportWhat integration tests report
Amount sent in dollars, provider expects centsAll green: the math is right and the request matches its mockThe provider's sandbox rejects the first charge, 100 times too small
9.98 charged instead of 9.99 on one basketA boundary sweep of the tax function catches it in millisecondsOne standard-basket test sails through

Neither level substitutes for the other. The wiring bug is invisible to mocks; the logic bug hides from any single realistic path.

How to split your effort

Write unit tests with the code, as a habit rather than a phase. Add integration tests at every seam where your system meets something real: each external API, the database layer, queues and jobs. Then reserve a thin top layer of end-to-end tests for full user journeys, since components can integrate pairwise and the assembled product can still fail a real signup. The levels of testing entry maps the full stack, and end-to-end testing vs integration testing continues this comparison one level up.


FAQs

Which should be written first, unit or integration tests?

Unit tests, because they exist while the code is being written and shape its design. Integration tests follow once there are two real pieces to connect.

Can integration tests replace unit tests?

No. A suite of only integration tests runs slowly, fails ambiguously, and skips logic edge cases that are trivial to sweep at the unit level. Thin both directions fail; the ratio is the point.

Is API testing unit or integration testing?

Testing an endpoint against a running service with a real database is integration testing. See API functional testing for how teams structure that layer.

What about sociable versus solitary unit tests?

Sociable unit tests let a class use its real collaborators in memory; solitary tests mock everything. Jay Fields coined the pair, and Martin Fowler's bliki spread it. Sociable tests blur toward integration testing, and the label matters less than knowing which contracts remain unverified.

Ship every deploy with confidence.

Bug0 gives you a dedicated AI QA engineer that tests every critical flow, on every PR, with zero test code to maintain. 200+ engineering teams already made the switch.

From $2,500/mo. Full coverage in 7 days.

Go on vacation. Bug0 never sleeps. The AI tests every commit, every deploy, every schedule. Your forward-deployed engineer reviews every failure and files the bugs. Coverage holds while you're off the grid.

Go on vacation.
Bug0 never sleeps.

The AI tests every commit, every deploy, every schedule. Your forward-deployed engineer reviews every failure and files the bugs. Coverage holds while you're off the grid.