Skip to content
Wojtek edited this page Jul 3, 2023 · 1 revision

Page version: 3.x

Global SetUp and TearDown

LightBDD offers ability to setup resources globally for the duration of entire test cycle and tear them down after all tests are executed.

RegisterGlobalSetup

With LightBDD 3.7.0 it is possible to register multiple SetUp and TearDown operations using configuration.ExecutionExtensionsConfiguration():

class MyGlobalDependency: IGlobalResourceSetUp
{
    public async Task SetUpAsync() { /* ... */}
    public async Task TearDownAsync() { /* ... */}
}

class ConfiguredLightBddScopeAttribute : LightBddScopeAttribute
{
    protected override void OnConfigure(LightBddConfiguration configuration)
    {
        configuration.ExecutionExtensionsConfiguration()
            .RegisterGlobalSetUp<MyGlobalDependency>()
            .RegisterGlobalSetUp("database", SetUpDatabase, TearDownDatabase)
            .RegisterGlobalTearDown("cleanup", RunCleanup);

        configuration.DependencyContainerConfiguration()
          .UseDefault(c =>
          {
              c.RegisterType<MyGlobalDependency>(InstanceScope.Single);
              /*...*/
          });
    }

    private async Task SetUpDatabase() { /* ... */ }
    private async Task TearDownDatabase() { /* ... */ }
    private async Task RunCleanup() { /* ... */ }
}

In the presented code, multiple SetUp/TearDown activities are registered.
Upon setup, activities are executed in registration order:

  1. MyGlobalDependency will be resolved from the DI (please note it is registered there as singleton) and it's SetUpAsync() method will be called first.
  2. If successful, then the SetUpDatabase() method will be called.

Upon tear down, activities are called in the reverse order:

  1. RunCleanup() will always be called
  2. then TearDownDatabase() will be called, but only if SetUpDatabase() was run,
  3. and then MyGlobalDependency.TearDownAsync() will be called, but only if SetUpAsync() was run.

There are following options available to register global activities:

  • RegisterGlobalSetUp<T>() for async set up and tear down of resources implementing IGlobalResourceSetUp, where TearDownAsync() is only called when corresponding SetUpAsync() was run.
  • RegisterGlobalSetUp(name, onSetUp, onTearDown) for set up and tear down (optional) methods, where onTearDown method is only called if corresponding setup was run. It is possible to register sync and async activities.
  • RegisterGlobalTearDown(name, onTearDown) for tear down methods (sync and async), where these methods will always be called.

LightBDDScope OnSetUp/OnTearDown

The LightBDDScope described on LightBDD Configuration page offers also OnSetUp() and OnTearDown() methods that can be overridden and use for global initialization and cleanup.

Prior to version 3.7.0, this was only available method but since this version it is no longer recommended due to following limitations:

  • These methods are not asynchronous thus async operations had to be written in a blocking manner.
  • In case multiple resources had to be created/destroyed upon global set up, the clean up logic had to ensure that all the resources are correctly torn down, even if some of them threw exceptions during that operation.

Scenario SetUp and TearDown

With LightBDD 3.7.0 it is possible to implement IScenarioSetUp and/or IScenarioTearDown interface on feature fixture classes to run async setup and async tear down for every scenario defined in this class.

Please note that these methods are called within Runner.Run*(), thus any resources initialized by OnScenarioSetUpAsync() won't have initialized state before calling Runner. The following code visualizes it:

// LightBDD.XUnit2 example
public class My_feature : FeatureFixture, IScenarioSetUp, IScenarioTearDown, IDisposable
{
    public My_feature(ITestOutputHelper output) : base(output)
    {
        TestOutput.WriteLine("#ctor");
    }

    Task IScenarioSetUp.OnScenarioSetUp()
    {
        TestOutput.WriteLine("#OnScenarioSetUp");
        return Task.CompletedTask;
    }

    Task IScenarioTearDown.OnScenarioTearDown()
    {
        TestOutput.WriteLine("#OnScenarioTearDown");
        return Task.CompletedTask;
    }

    [Scenario]
    public async Task My_scenario()
    {
        TestOutput.WriteLine("#My_scenario: before Runner.Run()");
        try
        {
            await Runner.RunScenarioAsync(_ => Given_step());
        }
        finally
        {
            TestOutput.WriteLine("#My_scenario: after Runner.Run()");
        }
    }

    private Task Given_step()
    {
        TestOutput.WriteLine("#Given step");
        throw new NotImplementedException();
    }

    void IDisposable.Dispose()
    {
        TestOutput.WriteLine("#Dispose");
    }
}
#ctor
#My_scenario: before Runner.Run()
SCENARIO: My scenario
#OnScenarioSetUp
  STEP 1/1: GIVEN step...
#Given step
  STEP 1/1: GIVEN step (Failed after 48ms)
#OnScenarioTearDown
#My_scenario: after Runner.Run()
#Dispose

Continue reading: DI Containers

Clone this wiki locally