Table of Contents

Dependency Injection in xUnit Tests

Important: If you only want to USE 2sxc / EAV, then you do NOT need this. This is meant for people who want to contribute to the source code of 2sxc and EAV.

Unit Tests which Don't use Dependency Injection

These unit tests are the simplest, and don't need any special setup. Example:

public void ManualListOfFunctions()
{
    var result = new FunFactString(null,
        [
            ("", _ => "Hello"),
            ("", s => s + " World"),
            ("", s => s + "!")
        ])
        .CreateResult();
    Equal("Hello World!", result);
}

Unit Tests which need Basic Dependency Injection by Namespace

These tests need DI to be setup, but don't need any configuration (such as Database connections).

First we need a startup class which does all the DI setup - in a Folder, so everything under it will use this:

using Microsoft.Extensions.DependencyInjection;
using ToSic.Lib.DI.SwitchableServices.Services;

namespace ToSic.Lib.DI.SwitchableServices;

public class Startup
{
    public void ConfigureServices(IServiceCollection services) =>
        services
            .AddTransient<ITestSwitchableService, TestSwitchableFallback>()
            .AddTransient<ITestSwitchableService, TestSwitchableKeep>()
            .AddTransient<ITestSwitchableService, TestSwitchableSkip>()
            .AddLibCore();
}

The tests in this folder and sub folders will automatically pick up the DI configuration from this startup class. This happens through the namespace, not through the physical folders.

This setup allows you to quickly reuse a setup, and specify it to be very specific to all the tests in the same namespace. In will then be available as a service in the test.

using ToSic.Lib.DI.SwitchableServices.Services;

namespace ToSic.Lib.DI.SwitchableServices;

public class VerifySwitchableService(ServiceSwitcher<ITestSwitchableService> switcher)
{

    [Fact]
    public void FindKeepService() =>
        Equal(TestSwitchableKeep.Name, switcher.Value.NameId);

    [Fact]
    public void Has3Services() =>
        Equal(3, switcher.AllServices.Count);

    [Fact]
    public void NotCreateBeforeButCreatedAfter()
    {
        False(switcher.IsValueCreated, "shouldn't be created at first");
        var x = switcher.Value;
        True(switcher.IsValueCreated, "should be created afterwards");
    }

    [Fact]
    public void FindFallbackByName() =>
        Equal(TestSwitchableFallback.Name, switcher.ByNameId(TestSwitchableFallback.Name).NameId);
}

Unit Tests with Basic Dependency Injection by Attribute

Every test class can also specify a specific DI setup by using an attribute. This is the preferred method, the syntax is like this:

[Startup(typeof(StartupTestsEavCore))]
public class LookUpEngineTests(DataBuilder dataBuilder) {
  // ...
}

Unit Tests with Dependency Injection and Startup with Scenario

In various cases you may need to setup a specific DI configuration for a test. For example, to specify a path or a DB connection. In xUnit this is done with Fixtures.

We also have a concept of TestScenarios which describe what DB and folders to use. This is run as a fixture with the DoFixtureStartup class.

[Startup(typeof(StartupTestFullWithDb))]
public class AccessItemsInAppState(IAppReaderFactory appReaderFactory, ITestOutputHelper output): IClassFixture<DoFixtureStartup<ScenarioBasic>>
{
  // Add any specific setup or tests related to TestScenarios here.
}
Tip

Where possible, the startup and fixture class should be a in a ...TestHelper.