Best examples of unit test failure examples: incorrect test setup
Before we talk about theory, let’s start with the pain. Here are real-world flavored examples of unit test failure examples: incorrect test setup that I’ve seen (or caused) in modern codebases.
Shared global state causing phantom failures
Imagine a test suite for a shopping cart service. One test adds an item and checks the total. Another test removes an item and checks the new total. Both tests pass when run alone, but when the full suite runs in CI, half the time one of them fails.
The root cause: a shared static Cart instance used across tests.
public class CartServiceTest {
private static final Cart cart = new Cart();
@Test
void addItem_updatesTotal() {
cart.clear();
cart.addItem("book", 10);
assertEquals(10, cart.getTotal());
}
@Test
void removeItem_updatesTotal() {
cart.clear();
cart.addItem("book", 10);
cart.removeItem("book");
assertEquals(0, cart.getTotal());
}
}
This looks safe because of the clear() calls, but in parallel or sharded test runs, the shared cart can be mutated by multiple tests at the same time. The incorrect test setup is the shared global fixture.
Better pattern: create a fresh Cart per test, either via a test framework lifecycle hook or plain instantiation inside each test.
This is one of the most common examples of unit test failure examples: incorrect test setup in large suites: shared mutable state masquerading as a “convenient” optimization.
Misconfigured mocks that never assert real behavior
Another classic example of unit test failure from incorrect test setup: mocks that are wired, but not actually used.
test('sends welcome email on signup', async () => {
const emailClient = {
send: jest.fn(),
};
const service = new SignupService(emailClient);
await service.signup('user@example.com');
expect(emailClient.send).toHaveBeenCalled();
});
Looks fine. But inside SignupService, someone refactored the constructor to create its own email client:
class SignupService {
constructor(private emailClient?: EmailClient) {}
async signup(email: string) {
const client = this.emailClient ?? new RealEmailClient();
await client.send(email, 'Welcome');
}
}
If the test isn’t passing in emailClient correctly (or a DI container overrides it later), the mock is never used. The test may pass locally with an in-memory mailer but fail in CI when environment variables cause RealEmailClient to hit a real SMTP server.
The incorrect setup: assuming that injecting a mock automatically guarantees it’s the one being used. This is a subtle example of unit test failure examples: incorrect test setup where the test doesn’t actually control its dependencies.
Time-dependent tests without a fixed clock
Any code that depends on the current time is a magnet for flaky tests if the setup is wrong.
Consider a subscription service that expires accounts after 30 days:
def is_expired(created_at: datetime, now: datetime | None = None) -> bool:
now = now or datetime.utcnow()
return (now - created_at).days >= 30
A test might be written like this:
def test_subscription_expires_after_30_days():
created_at = datetime.utcnow() - timedelta(days=30)
assert is_expired(created_at) is True
This passes most of the time. But if the test runs near a daylight saving time boundary or crosses midnight in UTC between the two utcnow() calls, the result can flip.
The incorrect test setup is using the real system clock instead of a fixed, deterministic time. A better setup would inject a frozen clock or pass an explicit now into is_expired.
This is one of the best examples of unit test failure caused by incorrect setup that only shows up under specific time conditions, making it hard to reproduce.
Hidden dependencies on configuration and environment
In many 2024–2025-era systems, configuration is pulled from environment variables, cloud secrets managers, or feature flag services. Tests often fail not because the logic is wrong, but because the environment is.
Take a feature flag check:
func IsNewCheckoutEnabled() bool {
return os.Getenv("NEW_CHECKOUT") == "on"
}
A test might assert:
func TestNewCheckoutEnabled(t *testing.T) {
os.Setenv("NEW_CHECKOUT", "on")
if !IsNewCheckoutEnabled() {
t.Fatalf("expected new checkout to be enabled")
}
}
This passes locally. In CI, another test sets NEW_CHECKOUT=off and never resets it. Now this test starts failing intermittently.
The incorrect test setup is failing to isolate environment variables between tests. This is a real example of how global process state can leak across tests and trigger unit test failure.
Modern test frameworks and CI systems increasingly encourage hermetic tests for exactly this reason. If you’re curious about the broader trend toward reproducible environments, the ideas are similar to what you see in scientific reproducibility discussions from places like Harvard’s data management resources.
Incorrect test data that masks real bugs
Sometimes the setup is wrong not because it’s unstable, but because it’s too convenient.
Imagine a function that calculates a discount but behaves differently at price boundaries:
public decimal ApplyDiscount(decimal price)
{
if (price >= 100)
return price * 0.9m; // 10% off
return price;
}
A test might use a single “easy” value:
[Fact]
public void ApplyDiscount_DoesNotThrow()
{
var result = ApplyDiscount(50);
Assert.Equal(50, result);
}
This test passes, and someone feels good that “discount is covered.” But the real bug is at the boundary, say price == 100.00m rounding issues in another currency conversion layer. Because the test setup uses only a mid-range value, it never exercises the risky path.
This is an example of unit test failure in a subtle way: the test doesn’t fail, but it fails to detect a real defect because the test data is incorrectly chosen. The setup is too narrow to be meaningful.
Good practice is to use boundary values and realistic data distributions. This mirrors advice you’ll find in software testing courses at universities like Carnegie Mellon, which often emphasize boundary value analysis and equivalence classes.
Database tests that aren’t actually isolated
Even in “unit” tests, many teams hit a real database or an embedded engine like SQLite or H2. The classic incorrect setup pattern: forgetting to clean up between tests.
Picture a user repository test:
@Test
fun `findByEmail returns user`() {
userRepository.save(User(email = "a@example.com"))
val user = userRepository.findByEmail("a@example.com")
assertNotNull(user)
}
On its own, this passes. But another test inserts the same email with different data and asserts a different behavior. If the database isn’t truncated or wrapped in a transaction per test, you get:
- Tests that pass when run individually
- Tests that fail or pass randomly in the full suite
The incorrect test setup here is the absence of a known starting state. In 2024–2025, more teams are using ephemeral databases in containers or in-memory engines with per-test transactions to avoid this, but the anti-pattern is still everywhere in legacy suites.
Async and concurrency tests without proper synchronization
Modern codebases are full of async/await, background tasks, and message queues. A very common example of unit test failure examples: incorrect test setup is asserting outcomes before async work finishes.
test('logs error on failure', () => {
const logger = { error: jest.fn() };
const service = new Service(logger);
service.doWork(); // async inside
expect(logger.error).toHaveBeenCalled();
});
If doWork is truly async (returns a promise or spawns a background task), the expectation may run before the error is logged. Locally, it might “work” because the event loop timing is different; in CI, it fails intermittently.
The incorrect setup is treating async code as if it were synchronous. The test should await the work or use a hook provided by the framework to wait for completion.
This pattern shows up in multi-threaded tests in languages like Java and C# as well, where missing join() or await calls cause races in assertions.
Misunderstood fixtures and test lifecycle
Many frameworks (JUnit, pytest, NUnit, xUnit, Jest, etc.) provide fixtures or lifecycle hooks. Misunderstanding these is another rich source of examples of unit test failure examples: incorrect test setup.
For instance, in pytest:
@pytest.fixture(scope="module")
def client():
return APIClient()
If tests mutate client state, that mutation persists across all tests in the module. Developers often intend to have a fresh client per test but accidentally use module or session scope, leading to hidden coupling and flaky behavior.
The incorrect setup is choosing fixture scopes that are too broad for mutable objects. The result is a test suite where order suddenly matters, which is exactly what you don’t want.
Patterns behind these examples of unit test failure
If you zoom out from these real examples of unit test failure examples: incorrect test setup, a few patterns repeat:
- Hidden shared state: globals, singletons, environment variables, static fields, long-lived fixtures
- Non-determinism: real clocks, random seeds, network calls, concurrency without synchronization
- Over-simplified test data: only “happy path” values, no boundaries, no edge cases
- Misconfigured indirection: mocks not injected correctly, DI containers overriding test wiring
- Misunderstood lifecycle: wrong fixture scope, missing teardown, not resetting state
Each of these patterns leads to tests that either:
- Fail intermittently (flaky tests)
- Pass when they should fail (false confidence)
- Fail for reasons unrelated to the code under test (noise)
From a quality perspective, the last two are especially dangerous. There’s research in software engineering that shows flaky tests erode developer trust in automated testing and slow down teams. While that literature often comes from academic sources like ACM Digital Library, the takeaway is practical: if your test setup is wrong, your tests stop being useful feedback.
How to avoid incorrect test setup in unit tests
You can’t eliminate every mistake, but you can adopt habits that make these examples of unit test failure examples: incorrect test setup far less likely.
Make each test own its state
Treat every test as if it runs in a dirty, shared process — because in CI, it probably does.
- Create fresh instances for each test instead of reusing mutable singletons.
- Reset or recreate environment variables, configuration, and static fields.
- Wrap database work in per-test transactions or truncate tables between tests.
If you’re working in a language with strong testing frameworks (JUnit 5, pytest, Jest, etc.), lean on built-in setup/teardown hooks, but keep the fixtures as small and local as you can.
Control time, randomness, and external systems
Any time you see now(), utcnow(), DateTime.Now, Math.random, or external calls, ask: “How will a test control this?”
- Inject clocks and random number generators instead of calling them directly.
- Use fakes or in-memory implementations for external services.
- Avoid hitting real networks, file systems, or cloud services in unit tests.
The mindset is similar to how health researchers think about controlling variables in a study; you want to know that the result is due to your code, not the environment. Organizations like the National Institutes of Health talk about controlling confounders in experiments; you’re doing the same thing in software.
Use realistic and boundary-focused test data
When you design test data, ask:
- What are the edge cases? Minimums, maximums, boundaries, empty values.
- What does real production data look like? Long strings, weird characters, big numbers.
Many of the best examples of unit test failure caused by incorrect setup come from overly “clean” test data that never hits real-world messiness.
Understand your test framework’s lifecycle
Take the time to learn:
- When setup and teardown run
- How fixtures are scoped
- How async tests are awaited
Misusing these features is behind a lot of the examples of unit test failure examples: incorrect test setup we’ve walked through. Reading the official docs for your framework is boring but pays off.
FAQ: examples of incorrect test setup in unit testing
Q: What are some common examples of incorrect test setup that cause flaky tests?
Common examples include shared mutable state between tests (static variables, global singletons), environment variables that aren’t reset, tests that rely on the real system clock, and database tests that don’t start from a clean state.
Q: Can you give an example of a unit test failure caused by mocks?
A classic example of unit test failure from incorrect test setup is when a mock is created but never actually injected into the code under test. The production code constructs its own real dependency, so the mock’s expectations never run, or a real network call sneaks into the test, causing random failures.
Q: How do I know if a failure is due to incorrect test setup or a real bug?
Patterns that point to incorrect setup include tests that pass when run alone but fail in the full suite, failures that depend on time of day or machine, and errors that mention environment configuration rather than business logic. If rerunning the same test without code changes flips the result, suspect the setup first.
Q: Are integration tests more vulnerable to incorrect setup than unit tests?
Integration tests tend to touch more external systems—databases, queues, file systems—so they have more ways to be misconfigured. But many of the same examples of unit test failure examples: incorrect test setup (like shared state and bad fixtures) apply to both.
Q: How often should I review my test setup patterns?
Any time you introduce new infrastructure (feature flags, new databases, new services) or see an uptick in flaky tests, it’s worth reviewing your setup patterns. Treat it like technical debt: the longer you wait, the harder it is to untangle.
If your test suite feels noisy, slow, or untrustworthy, chances are you’re living with some of the patterns we’ve talked through. The good news is that once you learn to recognize these examples of unit test failure examples: incorrect test setup, you start seeing them early—and fixing them before they hit CI.
Related Topics
Real-world examples of unit test failure due to race conditions in modern codebases
Best examples of unit test failure examples: incorrect test setup
Real-world examples of unit test failures from outdated libraries
Real-world examples of unit test failure due to improper exception handling
Real-world examples of unit test failures: mocking issues examples for modern codebases
Best examples of unit test failure examples: test doubles in real projects
Explore More Unit Testing Failures
Discover more examples and insights in this category.
View All Unit Testing Failures