unit testing
test execution
serial testing
parallel testing
software development

Execute unit tests serially rather than in parallel

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Running unit tests serially means executing one test after another instead of letting the test runner spread them across multiple workers or threads. Parallel execution is faster when tests are well isolated, but serial execution is sometimes the safer choice when the test suite or environment still contains shared state, order sensitivity, or tooling that is not concurrency-friendly.

Why Teams Sometimes Disable Parallel Execution

The main reason is not philosophical. It is practical. Some test suites break under parallelism because they share resources such as:

  • temporary files
  • database schemas
  • environment variables
  • static singletons
  • fixed network ports

In those cases, parallel execution exposes race conditions or hidden coupling between tests. Serial execution can make the suite deterministic again while you work toward better isolation.

That does not automatically make serial execution the ideal long-term answer. It often means the tests are revealing real independence problems. But it can still be the right operational choice for the moment.

Disable Parallelism at the Test Runner Level

Different frameworks provide different switches.

For JUnit 5, disable parallel execution in junit-platform.properties:

properties
junit.jupiter.execution.parallel.enabled = false

For pytest, the normal serial mode is simply to run without xdist. If a project uses xdist, set worker count to zero or remove the parallel flag:

bash
pytest

For xUnit in .NET, you can disable test parallelization at the assembly level:

csharp
using Xunit;

[assembly: CollectionBehavior(DisableTestParallelization = true)]

These settings do not change the tests themselves. They change how the runner schedules them.

Prefer Targeted Serialization When Possible

If only a subset of tests is problematic, it is often better to serialize just those tests rather than the entire suite.

For example, xUnit lets you group tests into collections that avoid parallel execution with each other:

csharp
1[Collection("DatabaseTests")]
2public class CustomerRepositoryTests
3{
4}

In JUnit, you might separate fragile integration tests from fast isolated unit tests and run the heavy group in a non-parallel task.

This approach keeps most of the suite fast while containing the shared-state problem to the areas that actually need protection.

The guiding principle is simple:

  • serialize the whole suite only if necessary
  • otherwise isolate and serialize the problematic subset

Serial Execution Does Not Replace Good Test Design

It is important to understand what serial execution does and does not solve.

It solves:

  • race conditions caused by simultaneous access
  • flaky ordering due to concurrent mutation
  • environment contention from shared resources

It does not solve:

  • tests that depend on previous test side effects
  • hidden global state that leaks between tests
  • incorrect assumptions about deterministic external systems

So if a serial suite still fails intermittently, the issue is probably deeper than parallel scheduling.

Serial execution is often a stabilizing step, not the final architecture of the test suite.

Keep Tests Isolated Even If You Run Them Serially

Even in serial mode, tests should clean up after themselves. A good serial suite still:

  • creates fresh fixtures where possible
  • resets shared state between tests
  • uses disposable files or databases
  • avoids dependence on execution order

If serial execution is the only reason the suite passes, that is a warning sign worth tracking.

For example, a database test should prefer a transaction rollback or a clean test database over relying on the fact that no other test is running at the same time.

Common Pitfalls

The biggest mistake is turning off parallel execution and treating the problem as solved forever. Very often, the suite is still fragile; it is just failing less visibly.

Another issue is serializing everything when only a small set of tests actually shares resources. That slows feedback unnecessarily.

Developers also sometimes confuse unit tests with integration tests. True unit tests should usually be parallel-friendly because they should not depend on global infrastructure.

Finally, if tests depend on execution order, serial mode may hide the issue temporarily. It is better to remove that coupling than to rely on a specific order staying stable.

Summary

  • Serial execution runs tests one at a time instead of concurrently.
  • It is often useful when tests share resources or exhibit race conditions.
  • Disable parallelism through the test runner, and prefer targeted serialization when possible.
  • Serial execution can stabilize a flaky suite, but it does not replace proper test isolation.
  • If the suite only passes in serial mode, that is usually a design signal worth addressing.

Course illustration
Course illustration

All Rights Reserved.