Skip to main content

Event Subscribing

Objects associated with your tests have the ability to subscribe to lifecycle events generated by TUnit.

Objects associated with your tests can mean:

  • The test class itself
  • A custom class constructor
  • Injected class parameter arguments
  • Injected method parameter arguments
  • Injected properties
  • Associated attributes

The interfaces they can implement are:

  • ITestRegisteredEventReceiver
  • ITestStartEventReceiver
  • ITestEndEventReceiver
  • ILastTestInClassEventReceiver
  • ILastTestInAssemblyEventReceiver
  • ILastTestInTestSessionEventReceiver

This can be useful especially when generating data that you need to track and maybe dispose later. By hooking into these events, we can do things like track and dispose our objects when we need.

Each attribute will be new'd up for each test, so you are able to store state within the fields of your attribute class.

The [ClassDataSource<T>] uses these events to do the following:

  • On Test Register > Increment Counts for Various Types (Global, Keyed, etc.)
  • On Test Start > Initialise any objects if they have the IAsyncInitializer interface
  • On Test End > If the object isn't shared, dispose it. Otherwise, decrement the count for the type.
  • On Last Test for Class > Dispose the object being used to inject into that specific class
  • On Last Test for Assembly > Dispose the object being used to inject into that specific assembly

Here's a simple Dependency Injection Class Constructor class subscribing to the TestEnd event in order to dispose the service scope when the test is finished:

public class DependencyInjectionClassConstructor : IClassConstructor, ITestEndEventReceiver
{
private readonly IServiceProvider _serviceProvider = CreateServiceProvider();
private AsyncServiceScope _scope;

public T Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class
{
_scope = _serviceProvider.CreateAsyncScope();

return ActivatorUtilities.GetServiceOrCreateInstance<T>(_scope.ServiceProvider);
}

public ValueTask OnTestEnd(TestContext testContext)
{
return _scope.DisposeAsync();
}

private static IServiceProvider CreateServiceProvider()
{
return new ServiceCollection()
.AddTransient<Class1>()
.AddTransient<Class2>()
.AddTransient<Class3>()
...
.BuildServiceProvider();
}
}