Introduction
I have been writing integration tests using MSTest recently and have encountered the need to control the level of parallelism required. MSTest, by default, runs your tests in parallel to maximise performance and reduce overall test execution time. This is great for independent unit tests, but it can quickly become a problem when you’re writing integration tests that access shared resources like databases, APIs, or cloud services. I recently encountered this issue when my integration test suite started failing randomly due to parallel tests competing for the same data or creating conflicting test data. The solution? Learning to control MSTest’s parallelism settings.
This post covers the various options available for controlling parallelism in your MSTest projects, from completely disabling parallel execution to fine-tuning which tests run in parallel and which ones run sequentially.
The Problem Scenario
Imagine you have a suite of integration tests for a customer management system. You have 20 integration tests that interact with a shared test database. Without parallelism control, MSTest might run all 20 tests simultaneously:
- Test A creates a customer with email “test@example.com”
- Test B simultaneously tries to create the same customer, causing a duplicate key error
- Test C queries the database expecting 5 records, but gets 25 because other tests are also inserting data
- Test D rolls back a transaction that Test E is still trying to read from
The result? Intermittent test failures that are hard to debug and unreliable CI/CD pipelines. By controlling parallelism, you can ensure tests that share resources run sequentially while still leveraging parallel execution where it’s safe.
Configuration Options
MSTest provides two main approaches for configuring parallelism:
- Per unit test file - Add the
Parallelizeattribute directly in your test files - Global configuration - Set it once in the
MSTestSettings.csconfiguration file for all tests
Understanding the Parallelize Attribute
The Parallelize attribute controls how tests run in parallel:
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)]
Parameters
Workers: Specifies the number of threads to run the tests. Set to
0to automatically use the number of CPU cores available on your machine. You can also specify an explicit number likeWorkers = 4to limit parallelism.Scope: Determines the level at which tests are parallelized:
ExecutionScope.MethodLevel- Runs all test methods in parallel, regardless of which class they belong toExecutionScope.ClassLevel- Runs test classes in parallel, but tests within the same class execute sequentially. Use this when tests within a class have interdependencies or shared state.
Applying Per Test File
Add the assembly attribute at the top of your test file:
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)]
namespace Test.UnitTests
{
Global Configuration with MSTestSettings.cs
For project-wide settings, create or update your MSTestSettings.cs file:
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)]
This approach keeps parallelization settings consistent across all test files in your project.
Running Tests Sequentially
To run all unit tests sequentially, simply remove the Parallelize assembly attribute from either the individual test file or the MSTestSettings.cs file, depending on which tests you want to run sequentially.
Mixed Parallelism: Running Specific Tests Sequentially
In some scenarios, you may want most tests to run in parallel, but certain tests need to execute sequentially (e.g., tests that access shared resources or databases). To achieve this:
- Keep the
Parallelizeassembly attribute in place - Mark specific test methods with the
[DoNotParallelize]attribute
[TestClass]
public class DatabaseTests
{
[TestMethod]
[DoNotParallelize]
public async Task TestDatabaseConnection()
{
// This test will run sequentially
}
[TestMethod]
public async Task TestDataRetrieval()
{
// This test will run in parallel with other parallel tests
}
}
Controlling Test Execution Order
While the best practice is to write independent unit tests that don’t rely on execution order, there are legitimate cases where ordering is necessary, such as:
- Integration tests with setup/teardown dependencies
- Tests that validate a sequence of operations
- End-to-end scenarios that must execute in a specific order
For more details on ordering tests, see the official Microsoft documentation on ordering unit tests.
Ensuring Consistent Ordering
To guarantee that test ordering works consistently in both Visual Studio Test Explorer and command-line test runs, add a .runsettings file to the root of your test project:
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<MSTest>
<OrderTestsByNameInClass>true</OrderTestsByNameInClass>
</MSTest>
</RunSettings>
This ensures tests within a class are ordered alphabetically by method name. You can then control order by naming your test methods accordingly (e.g., Test01_FirstStep, Test02_SecondStep).
Best Practices
- Default to parallel execution for unit tests to maximize performance
- Apply
[DoNotParallelize]selectively to tests where there are specific dependencies - Avoid test ordering when possible by ensuring tests are independent
- Monitor test execution time and adjust the
Workersparameter if needed
Conclusion
MSTest provides flexible options for controlling test parallelism, allowing you to balance execution speed with reliability. Start with parallel execution at the method level, and only introduce restrictions where necessary based on your specific test requirements.
Hope this helps and Happy Testing.
