Skip to content

Parallel Resource Constraints 🚦 ​

Prova runs tests in parallel by default to maximize performance. However, some tests require exclusive access to shared resources (like a database, file system path, or network port). Prova provides the [NotInParallel] attribute to handle these scenarios without disabling parallelism globally.

The Problem ​

If two tests try to write to the same file or database table simultaneously, they might fail or produce inconsistent results.

csharp
[Fact]
public async Task WriteToDatabase()
{
    // Race condition if run in parallel with another DB test!
    await _db.Insert("user1"); 
}

The Solution: [NotInParallel] ​

Use [NotInParallel("Key")] to declare that a test uses a specific resource. Prova ensures that no two tests with the same resource key run at the same time.

csharp
using Prova;

public class DatabaseTests
{
    [Fact]
    [NotInParallel("Database")] // Claims the "Database" lock
    public async Task Test1()
    {
        await _db.Insert("user1");
    }

    [Fact]
    [NotInParallel("Database")] // Waits for "Database" lock
    public async Task Test2()
    {
        await _db.Insert("user2");
    }
}

In this example:

  1. Test1 starts and acquires the "Database" lock.
  2. Test2 attempts to start, sees "Database" is locked, and waits.
  3. Test1 finishes and releases the lock.
  4. Test2 starts.

Non-Conflicting Parallelism ​

Tests with different resource keys (or no keys) continue to run in parallel!

csharp
[Fact]
[NotInParallel("Database")]
public void DB_Test() { ... }

[Fact]
[NotInParallel("FileSystem")]
public void File_Test() { ... } // Runs in parallel with DB_Test!

[Fact]
public void CPU_Test() { ... } // Runs in parallel with everything!

Multiple Resources ​

You can declare multiple resources for a single test. The test will wait until all locks are acquired.

csharp
[Fact]
[NotInParallel("Database", "FileSystem")]
public void IntegrationTest()
{
    // Has exclusive access to BOTH Database and FileSystem
}

Class-Level Constraints ​

You can apply the attribute to a class to enforce the constraint on all tests within that class.

csharp
[NotInParallel("Database")]
public class UserRepoTests
{
    [Fact]
    public void AddUser() { ... } // Implicitly claims "Database"

    [Fact]
    public void DeleteUser() { ... } // Implicitly claims "Database"
}

TIP

Using [NotInParallel] is much more performant than disabling parallelism entirely with [DoNotParallelize], because it allows non-conflicting tests to keep running fast.

Released under the MIT License.