NotoriousTest 2.3.0-beta.9

This is a prerelease version of NotoriousTest.
There is a newer version of this package available.
See the version list below for details.
dotnet add package NotoriousTest --version 2.3.0-beta.9                
NuGet\Install-Package NotoriousTest -Version 2.3.0-beta.9                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NotoriousTest" Version="2.3.0-beta.9" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add NotoriousTest --version 2.3.0-beta.9                
#r "nuget: NotoriousTest, 2.3.0-beta.9"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install NotoriousTest as a Cake Addin
#addin nuget:?package=NotoriousTest&version=2.3.0-beta.9&prerelease

// Install NotoriousTest as a Cake Tool
#tool nuget:?package=NotoriousTest&version=2.3.0-beta.9&prerelease                

NuGet NuGet Downloads License .NET GitHub stars

Notorious Test provide a simple way to isolate integration tests. Based on XUnit.

Contact

Have questions, ideas, or feedback about NotoriousTests? Feel free to reach out! I'd love to hear from you. Here's how you can get in touch:

The discussions tabs is now opened ! Feel free to tell me if you use the package here : https://github.com/Notorious-Coding/Notorious-Test/discussions/1 !

Summary

Support

  • Net6+

Features

  • Easy share of test infrastructure.
  • Easy building of test framework.
  • Complete isolation of integration tests.
  • Simple implementation.
  • Based on XUnit.

Motivation

The goal is to provide a way to make integration tests without worrying about data collision, side effects, and infrastructure sharing.

Changelog

You can find the changelog here.

Setup

First, install NuGet. Then, install NotoriousTest from the package manager console:

PM> Install-Package NotoriousTest

Or from the .NET CLI as:

dotnet add package NotoriousTest

Base functionalities

Infrastructures

An infrastructure is a piece of hardware or software that is necessary for your app to work. For example, a SQL Server Database is an infrastructure. NotoriousTests provide a way to design your infrastructures by creating classes for each of them.

using NotoriousTest.Common.Infrastructures.Sync;

public class SQLServerDBInfrastructure : Infrastructure
{
    public SQLServerDBAsyncInfrastructure(bool initialize = false) : base(initialize){}
    public override void Initialize()
    {
        // Here you can create the database
    }

    public override void Reset()
    {
        // Here you can empty the database
    }
    public override void Destroy()
    {
        // Here you can destroy the database
    }
}

Async version :

using NotoriousTest.Common.Infrastructures.Async;

// Async version
public class SQLServerDBAsyncInfrastructure : AsyncInfrastructure
{
    public SQLServerDBAsyncInfrastructure(bool initialize = false) : base(initialize){}

    public override Task Initialize()
    {
        // Here you can create the database
    }

    public override Task Reset()
    {
        // Here you can empty the database
    }
    public override Task Destroy()
    {
        // Here you can destroy the database
    }
}

As you can see, an infrastructure is made of 3 main lifecycle method.

  • Initialize : You can call this method to start an infrastructure or set the initialize boolean in the constructor at true.

  • Reset : you can call this method to reset an infrastructure. Reset mean to put the infrastructure in an empty state without recreating it from scratch (essentialy for perfomances).

  • Destroy : you can call this method to destroy an infrastructure, Destroy mean to completly delete the infrastructure.

Once you made it, you can use this infrastructure as standalone in your tests :

[Fact]
public async Task Database_Creation()
{
    // Dont forget to put initialize at true, so you dont have to call **Initialize**
    await using (var db = new SQLServerDBAsyncInfrastructure(initialize: true))
    {
        // Test logic here...
    }
    // Disposing an infrastructure will automatically call Destroy()
}

Even if using an infrastructures in standalone can be easy, it should be a best practices to use them inside an Environment. Let's see why and how.

Environment

We have seen how we could use an infrastructure directly within a tests, but it will become really difficult to maintain if we do this for every test and for every infrastructure.

That's why we provide this environment feature :

An environment is a collection of infrastructure drived by tests' lifecycle.

Cycle de vie des infrastructures

Let's create an environment :

public class SampleEnvironment : Environment
{
    public override void ConfigureEnvironment()
    {
        // Add all your infrastructure here.
        AddInfrastructure(new DatabaseInfrastructure());
    }
}

Async version :

public class SampleEnvironment : AsyncEnvironment
{
    public override Task ConfigureEnvironmentAsync()
    {
        // Add all your infrastructure here.
        AddInfrastructure(new DatabaseInfrastructure());

        return Task.CompletedTask;
    }
}

Then, make your test class inherit from IntegrationTest.

public class UnitTest1 : IntegrationTest<SampleEnvironment>
{
    public UnitTest1(SampleEnvironment environment) : base(environment)
    {
    }

    [Fact]
    public async void MyTest()
    {
        // Access infrastructure by calling
        SQLServerDBAsyncInfrastructure infrastructure = await CurrentEnvironment.GetInfrastructure<SQLServerDBAsyncInfrastructure>();
    }
}

Async version:

public class UnitTest1 : AsyncIntegrationTest<SampleEnvironment>
{
    public UnitTest1(SampleEnvironment environment) : base(environment)
    {
    }

    [Fact]
    public async Task MyTest()
    {
        // Access infrastructure by calling
        SQLServerDBAsyncInfrastructure infrastructure = await CurrentEnvironment.GetInfrastructureAsync<SQLServerDBAsyncInfrastructure>();
    }
}

Advanced functionalities

Ordering infrastructures execution

In certain contexts, the execution order of infrastructures within an environment can be crucial.

To address this, all infrastructures provide an optional Order property. This allows specifying the order of initialization, reset, and teardown of infrastructures.

By default, infrastructures without a defined order will be prioritized and executed first.

❗ The only exception is WebApplicationInfrastructure, which will ALWAYS be executed last, as it depends on other infrastructures' configuration.

Here's an example :

    public class DatabaseInfrastructure : AsyncConfiguredInfrastructure<Configuration>
    {
        // Use this property to order execution.
        public override int? Order => 1;
        public DatabaseInfrastructure(bool initialize = false): base(initialize)
        {
        }

        public override Task Destroy()
        {
            return Task.CompletedTask;
        }

        public override Task Initialize()
        {
            return Task.CompletedTask;
        }

        public override Task Reset()
        {
            return Task.CompletedTask;
        }
    }

Advanced Control Over Infrastructure Resets

By default, infrastructures are reset between each test to ensure data isolation. However, in certain scenarios -such as multi-tenant applications where isolation is ensured by design- automatic resets may not be necessary.

With the AutoReset option, you can disable the automatic reset for a specific infrastructure:

    public class SampleEnvironment : AsyncWebEnvironment<Program, Configuration>
    {
        public override Task ConfigureEnvironmentAsync()
        {
            AddInfrastructure(new DatabaseInfrastructure()
            {
                AutoReset = false
            });
            AddWebApplication(new SampleProjectApp());

            return Task.CompletedTask;
        }
    }

When AutoReset is set to false, the Reset method of the specified infrastructure will be skipped during the test lifecycle. This can save significant time and resources in scenarios where resetting is unnecessary.

⚠️ Note: Use this option carefully. Ensure that tests are designed to avoid dependencies on leftover data unless explicitly intended.

Configuration

In some cases, you will need to handle configuration from your infrastructures, such as connection string, or secrets, etc. Notorious Tests provides a nice way to produce and consume configuration within infrastructures.

Here's how :

Configurable Infrastructures

To create a configurable infrastructure, the only thing you need to do is to inherits from ConfiguredInfrastructure or AsyncConfiguredInfrastructure.

    public class DatabaseInfrastructure : ConfiguredInfrastructure<Configuration>
    {

        public DatabaseInfrastructure(bool initialize = false) : base(initialize)
        {
        }

        public override int Order => 1;

        public override void Destroy(){}

        public override void Initialize()
        {
            /// Initialize sql server.
            Configuration.DatabaseConfiguration = new DatabaseConfiguration()
            {
                ConnectionString = "Test"
            };
        }

        public override void Reset(){}
    }

As you can see, this infrastructure will produce configuration after initializing the sql server. Configuration is available as public so you can then access it when using this infrastructure.

Generic type for configuration is not mandatory, Configuration object will be a Dictionary<string, string>

    public class DatabaseInfrastructure : ConfiguredInfrastructure
    {

        public DatabaseInfrastructure(bool initialize = false) : base(initialize)
        {
        }

        public override int Order => 1;

        public override void Destroy(){}

        public override void Initialize()
        {
            /// Initialize sql server.
            Configuration.Add("DatabaseConfiguration.ConnectionString", "Test");
        }

        public override void Reset(){}
    }

Now, you can access your infrastructure configuration directly within a test :

[Fact]
public async Task Test2()
{
    await using (var db =  new DatabaseInfrastructure(initialize: true))
    {
        var cs = db.Configuration.DatabaseConfiguration.ConnectionString;
    }
}
Configurable Environment

You could use ConfiguredInfrastructure within Environment.

First, inherit from ConfiguredEnvironment instead of Environment. (from AsyncConfiguredEnvironment instead of AsyncEnvironment).

public class SampleEnvironment : ConfiguredEnvironment<Configuration>
{
    public override void ConfigureEnvironment()
    {
        // Add all your infrastructure here.
        AddInfrastructure(new DatabaseInfrastructure());
    }
}
// Or without generic type
public class SampleEnvironment : ConfiguredEnvironment{
    public override void ConfigureEnvironment()
    {
        // Add all your infrastructure here.
        AddInfrastructure(new DatabaseInfrastructure());
    }
}

Within any ConfiguredEnvironment, you would use the public property EnvironmentConfiguration to access produced configuration from infrastructures.

❗ Inside an environment, configuration object should represent the whole app configuration. It will move from infrastructures to infrastructures, and each infrastructure will edit its own part of the configuration.

Web

Within web applications, you will certainly need to add a WebApplication that work in background so your tests can call an actual API.

NotoriousTests provide everything so you donc need to scratch your head too much.

Web Application Infrastructure

First, you will need to create an WebApplication.

internal class SampleProjectApp : WebApplication<Program>{}

This is actually a WebApplicationFactory provided by .NET (See microsoft doc for more information.)

Here you can override everything you need for your app to be fully functional.

Then, let's create an WebApplicationInfrastructure and pass our WebApplication.

:information: Within a WebEnvironment, creating an infrastructure is optional. You can directly pass a WebApplication. But this can be usefull to add initialization/reset/destroy behaviors.

    internal class SampleProjectWebApplicationInfrastructure : WebApplicationInfrastructure<Program, Configuration>
    {
        public SampleProjectWebApplicationInfrastructure(Dictionary<string, string> configuration)
            : base(new SampleProjectApp())
        {
        }
    }

    // Without a generic type, configuration will be a Dictionary<string, string>.
    internal class SampleProjectWebApplicationInfrastructure : WebApplicationInfrastructure<Program>
    {
        public SampleProjectWebApplicationInfrastructure(Dictionary<string, string> configuration)
            : base(new SampleProjectApp())
        {
        }
    }

:information: Configuration passed will be automaticaly added as configuration on your web app, making it accessible within IConfiguration object in Program.cs.

Now, within your test, you can start your WebApplicationInfrastructures like any other infrastructure, and access HttpClient:

[Fact]
public async Task Test2()
{
    await using (var app = new SampleProjectWebApplicationInfrastructure())
    {
        HttpClient? client = app.HttpClient;

        HttpResponseMessage response = await client!.GetAsync("api/weather");
        Assert.True(response.IsSuccessStatusCode);

        string content = await response.Content.ReadAsStringAsync();
    }
}
Web Environment

WebApplication or WebApplicationInfrastructure can be used within WebEnvironment.

    public class SampleEnvironment : AsyncWebEnvironment<Program, Configuration>
    {
        public override Task ConfigureEnvironmentAsync()
        {
            AddInfrastructure(new DatabaseInfrastructure());
            AddWebApplication(new SampleProjectApp());
            // OR
            AddWebApplication(new SampleProjectWebApplicationInfrastructure());

            return Task.CompletedTask;
        }
    }

Your web application will start automatically at the start of a test campaign, and configuration produced by infrastructures will automaticaly be added as an InMemoryCollection to your WebApplication.

Then, in your test, you can use GetWebApplication to access the HttpClient :

[Fact]
public async Task Test2()
{
    HttpClient? client = (await CurrentEnvironment.GetWebApplication()).HttpClient;

    HttpResponseMessage response = await client!.GetAsync("api/weather");
    Assert.True(response.IsSuccessStatusCode);

    string content = await response.Content.ReadAsStringAsync();
}

Nice ! Good job, now, your integration test are isolated from each other.

TestContainers

NotoriousTest.TestContainers is now available as a separate package.

Install NotoriousTest from the package manager console:

PM> Install-Package NotoriousTest.TestContainers

Or from the .NET CLI as:

dotnet add package NotoriousTest.TestContainers

This package provides classes that automatically start and stop the container at the beginning and end of the test campaign! It introduces three new classes:

  • DockerContainerAsyncInfrastructure<TContainer> : Standard infrastructure.
  • ConfiguredDockerContainerAsyncInfrastructure<TContainer>: Infrastructure handling configuration as a dictionary.
  • ConfiguredDockerContainerAsyncInfrastructure<TContainer, TConfiguration> : Infrastructure handling a configuration object.

❗ Since TestContainers doesn't support synchronous code, theses classes are only available in an AsyncEnvironment.

Here's an example :


public class SqlServerContainerInfrastructure : DockerContainerAsyncInfrastructure<GenericContainer>
{
    public override Container {get; init;} = new MsSqlBuild().Build();

    public SampleDockerContainer(bool initialize = false) : base(initialize)
    {
    }

    public override Task Reset()
    {
        return Task.CompletedTask;
    }
}

Hands-On Examples

Get started quickly with practical examples available in the Samples folder. These examples demonstrate how to set up and use NotoriousTests for real-world scenarios.

What's included:

  • SqlServerInfrastructures.cs
    Learn how to manage your SQL Server database using Respawn, TestContainers and plain SQL for creating, destroying, and resetting your database seamlessly.
  • TestWebApplication.cs
    See how to configure a WebApplicationFactory with in-memory configuration for fast and isolated tests.
  • TestEnvironment
    Understand how to set up environments to manage multiple infrastructures effortlessly.
  • SampleTests.cs
    Dive into this file to see how to access infrastructures and use them in your tests.
  • Program.cs
    Explore how the SqlServerInfrastructure generates configuration for the Web Application.

Other nugets i'm working on

  • NotoriousClient : Notorious Client is meant to simplify the sending of HTTP requests through a fluent builder and an infinitely extensible client system.
  • NotoriousModules : Notorious Modules provide a simple way to separate monolith into standalone modules.
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on NotoriousTest:

Package Downloads
NotoriousTest.TestContainers

A TestContainers integration with NotoriousTest.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.0.0-beta.28 51 2/22/2025
2.3.0 152 2/10/2025
2.3.0-beta.25 42 2/22/2025
2.3.0-beta.23 53 2/14/2025
2.3.0-beta.20 57 2/12/2025
2.3.0-beta.18 53 2/10/2025
2.3.0-beta.16 53 2/9/2025
2.3.0-beta.14 54 2/9/2025
2.3.0-beta.9 57 2/9/2025
2.2.0 85 2/9/2025
2.2.0-beta.7 50 2/9/2025
2.2.0-beta.5 49 2/9/2025
2.2.0-beta.4 56 2/9/2025
2.2.0-beta.2 51 2/9/2025
2.1.0 115 2/2/2025
2.0.0 124 9/4/2024
1.3.4 137 5/5/2024
1.3.3 127 5/5/2024
1.3.2 125 5/5/2024
1.2.1 163 1/17/2024
1.2.0 167 10/3/2023
1.1.0 133 9/28/2023
1.0.0 146 9/19/2023