Injectable Class Data Source
The ClassDataSource attribute is used to instantiate and inject in new classes as parameters to your tests and/or test classes.
The attribute takes a generic type argument, which is the type of data you want to inject into your test.
It also takes an optional Shared argument, controlling whether you want to share the instance among other tests.
This could be useful for times where it's very intensive to spin up lots of objects, and you instead want to share that same instance across many tests.
Ideally don't manipulate the state of this object within your tests if your object is shared. Because of concurrency, it's impossible to know which test will run in which order, and so your tests could become flaky and undeterministic.
Options are:
Shared = SharedType.None (Default)â
The instance is not shared ever. A new one will be created for you. This is the default if Shared is not specified.
Shared = SharedType.PerClassâ
The instance is shared for every test in the same class as itself, that also has this setting.
Shared = SharedType.PerAssemblyâ
The instance is shared for every test in the same assembly as itself, that also has this setting.
Shared = SharedType.PerTestSessionâ
The instance is shared for every test in the current test session, meaning it'll always be the same instance.
Shared = SharedType.Keyedâ
When using this, you must also populate the Key argument on the attribute.
The instance is shared for every test that also has this setting, and also uses the same key.
Initialization and TearDownâ
If you need to do some initialization or teardown for when this object is created/disposed, simply implement the IAsyncInitializer and/or IAsyncDisposable interfaces
Example
public class MyTestClass
{
[Test]
[ClassDataSource<WebApplicationFactory>(Shared = SharedType.PerTestSession)]
public void MyTest(WebApplicationFactory webApplicationFactory)
{
}
public record WebApplicationFactory : IAsyncInitializer, IAsyncDisposable
{
// Some properties/methods/whatever!
public async Task InitializeAsync()
{
await StartServer();
}
public async ValueTask DisposeAsync()
{
await StopServer();
}
}
}
Class Data Source Overloads
If you are using an overload that supports injecting multiple classes at once (e.g. ClassDataSource<T1, T2, T3>) then you should specify multiple SharedTypes in an array and keys where applicable.
Important: The Keys array is positional - each index corresponds to the type at that position in the generic parameters. Only types with SharedType.Keyed need keys; other positions can be empty strings or omitted.
E.g.
[Test]
[ClassDataSource<Value1, Value2, Value3, Value4, Value5>
(
Shared = [SharedType.PerTestSession, SharedType.Keyed, SharedType.PerClass, SharedType.Keyed, SharedType.None],
Keys = ["", "Value2Key", "", "Value4Key", ""]
// Index 0: Value1 (PerTestSession) - empty string (no key needed)
// Index 1: Value2 (Keyed) - "Value2Key"
// Index 2: Value3 (PerClass) - empty string (no key needed)
// Index 3: Value4 (Keyed) - "Value4Key"
// Index 4: Value5 (None) - empty string (no key needed)
)]
public class MyType(Value1 value1, Value2 value2, Value3 value3, Value4 value4, Value5 value5)
{
}