Unit Tests vs Integration Tests
Fast recall
Quick revision
Core answer
ActionDispatch::IntegrationTest or RSpec request spec can exercise routing, controller behaviour, rendering, and persistence together. Unit tests are fast and precise; integration tests give broader confidence. A healthy suite uses both according to the risk being tested.Whether you know unit tests check one piece in isolation (fast, pinpoint), integration tests check parts together (slow, catches wiring), and you balance the pyramid.
Only integration tests (slow, hard to localize) or only unit (misses wiring bugs) — balance the pyramid.
Unit = one piece in isolation (fast, pinpoint). Integration = parts together (routing+controller+model+DB, catches wiring). Balance the pyramid.
Interview-ready guide
Interview answer
A unit test exercises one small responsibility and controls collaborators outside that responsibility. A shipping-fee test can call ShippingFee.for(total) with plain values and avoid routing, a database, or a courier API. When it fails, the search area is small and many boundary cases remain cheap to run.
An integration test lets several real parts cooperate. In Rails, ActionDispatch::IntegrationTest or a request spec may cross the router, controller filters, application objects, database, and renderer. It catches broken wiring, serialization, transactions, and configuration that an isolated rule test cannot see.
The labels describe a spectrum. A model test that writes associated records and runs callbacks already uses database integration. A request test with every collaborator stubbed may prove less integration than its filename suggests. Ask which dependencies are real and which failure the test must honestly detect.
A healthy suite combines many focused decision tests with selected integration flows. The runnable example tests the shipping threshold alone, then lets Checkout use the real fee calculation so a second test verifies the objects cooperate. A Rails request test applies the same idea across the HTTP stack.
Unit Tests vs Integration Tests
Direct Answer
A unit test checks one small unit of behaviour with most external boundaries controlled; an integration test checks that multiple real parts work together. In Rails, a focused test of a pricing object is unit-like, while an ActionDispatch::IntegrationTest or RSpec request spec can exercise routing, controller behaviour, rendering, and persistence together. Unit tests are fast and precise; integration tests give broader confidence. A healthy suite uses both according to the risk being tested.
Deep Dive & Architecture
Test scope describes how much real collaboration occurs. A calculation with plain values has a narrow boundary. Adding Active Record, the database, a job adapter, or an HTTP client widens it. The useful distinction is not whether the production subject is a class, but which dependencies the test lets participate.
A controlled dependency can make a focused test deterministic. A fake exchange-rate client lets pricing tests cover timeout or invalid-rate behaviour without a network. That test must still respect the real client's local contract, and a separate integration or contract test should confirm the adapter speaks to its actual boundary correctly.
Broad tests are best spent on paths whose connections matter: authentication plus authorization, parameter parsing plus validation errors, a successful transaction, or an external adapter's serialization. Repeating every small rule at that level creates slow setup without adding a new relationship to verify.
When a test fails, its scope determines the search area. Narrow tests give precise feedback; broad tests show the product path is broken. Keeping both layers allows a broad failure to reveal impact and a focused failure to reveal the responsible rule.
Code Example
# Focused unit-like test
RSpec.describe ShippingFee do
it 'waives shipping at the free-delivery threshold' do
expect(described_class.for(order_total: 50)).to eq(0)
end
end
# Integration boundary
RSpec.describe 'Checkout', type: :request do
it 'returns the calculated delivery charge' do
post '/checkout', params: { order_total: 40 }, as: :json
expect(response).to have_http_status(:ok)
expect(response.parsed_body['shipping_fee']).to eq(5)
end
end
Common Mistakes & Pitfalls
Only integration tests (slow, hard to localize) or only unit (misses wiring bugs) — balance the pyramid.
Check your understanding
Answer these without looking back at the article.
- Can a model test be an integration test?
- What problem appears when a fake does not match the real collaborator?
- Which application flows deserve broader integration coverage?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.