---
title: Create unit tests
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

When creating [automated tests](https://docs.kentico.com/documentation/developers-and-admins/customization/write-automated-tests.md), use unit tests whenever possible. Unit tests run without external resources such as a database. They execute faster than other types of automated tests and are the easiest to integrate into your development process.

A common problem with unit tests is that code needs to connect to a database, either directly or indirectly through external dependencies. Unit tests where this problem occurs throw the following exception upon execution:

**"System.InvalidOperationException: The ConnectionString property has not been initialized"**

In many cases, you can solve the problem by preparing faked data for your unit tests.

> **Info:** If you cannot fake the data required for a test, create an integration or isolated integration test instead. Integration tests can work with a database, but have significantly slower execution. See:
>
> - [Create integration tests with a connection string](https://docs.kentico.com/documentation/developers-and-admins/customization/write-automated-tests/create-integration-tests.md)
> - [Create isolated integration tests](https://docs.kentico.com/documentation/developers-and-admins/customization/write-automated-tests/create-isolated-integration-tests.md)

## Fake Info and Provider objects

The Xperience API uses _Info_ and _Provider_ classes to manage data stored in database tables. See [Database table API](https://docs.kentico.com/documentation/developers-and-admins/api/database-table-api.md) for more information.

To create fake data for your unit tests, call the **Fake** method from the **UnitTests** base class (must be inherited by all unit test classes using the _CMS.Tests_ library). The method prepares a faked instance of the provider with in-memory data. You can then use the provider's methods to **get**, **create**, or **update** data in your unit tests.

Code that works with Info objects typically receives the corresponding provider as `IInfoProvider<TInfo>` through [dependency injection](https://docs.kentico.com/documentation/developers-and-admins/development/website-development-basics/dependency-injection.md). Register the faked provider using `Service.Use` to make it available to such code.

> **Note:** Faked providers do **not** allow you to use methods that **delete data**. If you need to test methods that delete data, use [isolated integration tests](https://docs.kentico.com/documentation/developers-and-admins/customization/write-automated-tests/create-isolated-integration-tests.md).

```csharp title="Example"
using CMS.Core;

using CMS.DataEngine;

using CMS.Membership;

using CMS.Tests;

using NUnit.Framework;

[TestFixture]
public class MyUnitTests : UnitTests
{
    protected override void RegisterTestServices()
    {
        // Always call the base implementation to keep the default test service registrations
        base.RegisterTestServices();

        // Prepares faked data for the member provider
        // The tested code depends on IInfoProvider<MemberInfo>, so the generic provider is faked
        IInfoProviderFake<MemberInfo, IInfoProvider<MemberInfo>> memberFake = Fake<MemberInfo, IInfoProvider<MemberInfo>>().WithData(
            new MemberInfo
            {
                MemberID = 1,
                MemberName = "FakeMember"
            });

        // Registers the fake so that it is injected into code that depends on IInfoProvider<MemberInfo>
        Service.Use<IInfoProvider<MemberInfo>>(memberFake.ProviderObject);
    }

    [Test]
    public void MyTest()
    {
        IInfoProvider<MemberInfo> memberProvider = Service.Resolve<IInfoProvider<MemberInfo>>();

        // Returns the faked data instead of accessing the database
        MemberInfo? member = memberProvider.Get(1);

        Assert.That(member?.MemberName, Is.EqualTo("FakeMember"));
    }
}
```

In the example, the `IInfoProvider<MemberInfo>` provider is faked with specific data. Code that resolves `IInfoProvider<MemberInfo>` then returns the faked data instead of accessing the database. The code behaves as if there were a single member named _FakeMember_ in the database.

The second type parameter of the `Fake` method must match the provider type that the tested code depends on:

- For code that uses the [generic provider](https://docs.kentico.com/documentation/developers-and-admins/api/database-table-api.md#generic-info-provider), pass `IInfoProvider<TInfo>`, for example `Fake<MemberInfo, IInfoProvider<MemberInfo>>()`.
- For code that uses a [dedicated provider class](https://docs.kentico.com/documentation/developers-and-admins/api/database-table-api.md#dedicated-provider-classes), pass the provider class, for example `Fake<MemberInfo, MemberInfoProvider>()`.

You do **not** need to clean up the faked data before or after running tests. Tests inherited from the **UnitTests** base class automatically reset all faked data upon initialization and cleanup.

> **Tip:** Every `WithData` call resets the faked data and replaces it with the provided items. To add items to a provider that is already faked, for example in an individual test that needs extra data, call `IncludeData` on the fake instead.

### Fake Info metadata only

Calling the constructor of an _Info_ class requires access to metadata stored in the database. If your unit tests do not require faked Providers with data prepared in advance, but directly create new instances of Info classes, you can fake only the Info metadata.

To prepare faked metadata for an Info class, call the **Fake** method with a single generic type parameter matching the given Info class.

```csharp title="Faking metadata only"
using CMS.Membership;

using CMS.Tests;

using NUnit.Framework;

[Test]
public void FakeMetadata_AllowsInfoInstantiation()
{
    Fake<MemberInfo>();

    MemberInfo member = new();
    member.MemberName = "TestMember";

    Assert.That(member.MemberName, Is.EqualTo("TestMember"));
}
```

## Substitute other services

Faking covers Info objects and their providers. To replace any other service that your code resolves from the Xperience [service container](https://docs.kentico.com/documentation/developers-and-admins/development/website-development-basics/dependency-injection.md), override the **RegisterTestServices** method and call `Service.Use`. Always call the base implementation first to keep the default test registrations.

```csharp title="Registering a test double for a service"
using CMS.Core;

using CMS.Tests;

using NUnit.Framework;

[TestFixture]
public class RegisterTestServicesUnitTests : UnitTests
{
    private IMyCustomService myCustomService = null!;

    protected override void RegisterTestServices()
    {
        // Always call the base implementation to keep the default test service registrations
        base.RegisterTestServices();

        myCustomService = new DeterministicCustomService();
        Service.Use<IMyCustomService>(myCustomService);
    }

    [Test]
    public void RegisterTestServices_ResolvesCustomService()
    {
        IMyCustomService resolvedService = Service.Resolve<IMyCustomService>();

        Assert.That(resolvedService, Is.SameAs(myCustomService));
    }
}
```

> **Tip:** If your service only depends on your own interfaces and never calls the Xperience API, you can instantiate it directly in a plain NUnit test class without any _CMS.Tests_ base class. Such tests do not need the Xperience service container at all.

## Test code that retrieves content

Code that retrieves content items and website channel pages through the [Content item API](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api.md) requires a database. Faking covers Info objects and their providers only, so you cannot fake content items or pages the way you fake other Xperience data.

Choose an approach based on what you need to verify:

|                                                                         |
| ----------------------------------------------------------------------- |
| [Substitute the retrieval services](#substitute-the-retrieval-services) |
| [Isolated integration tests](#isolated-integration-tests)               |

Substituting the retrieval services replaces content retrieval with prepared data, so it does not verify that your query returns the correct content. Combine it with a small number of isolated integration tests that cover the queries themselves.

### Substitute the retrieval services

Content retrieval in Xperience goes through the following services:

- **IContentRetriever** – the recommended service for retrieving content in application code. See [Content retriever API](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/content-retriever-api.md).
- **IContentQueryExecutor** – the lower-level service used to execute content item queries.

Register a test double for the service that your code uses in the [RegisterTestServices](#substitute-other-services) override, and return prepared content from it.

With this approach, the tested code still builds the real query and calls the Xperience API. The query never reaches the database, so the tests verify only what your code does with the returned content.

### Isolated integration tests

Content queries are one of the cases that require a real database. See [Create isolated integration tests](https://docs.kentico.com/documentation/developers-and-admins/customization/write-automated-tests/create-isolated-integration-tests.md).

## Test the service container itself

For tests that only need the service container (registration and resolution) without the faking infrastructure, inherit from **IsolatedContainerUnitTests**. This class provides a lightweight alternative to `UnitTests`:

- Manages the container lifecycle (reset, register, build, dispose) per test fixture
- Does **not** reset application state between individual tests
- Does **not** provide `Fake<>()` methods
- Executes faster than `UnitTests`

```csharp title="Using IsolatedContainerUnitTests"
using CMS.Core;

using CMS.Tests;

using NUnit.Framework;

[TestFixture]
public class IsolatedContainerUnitTestsExample : IsolatedContainerUnitTests
{
    protected override void RegisterTestServices()
    {
        // Registers services required by tests in this fixture
        Service.Use<IMyCustomService, DeterministicCustomService>();
    }

    [Test]
    public void IsolatedContainer_ResolvesRegisteredService()
    {
        IMyCustomService service = Service.Resolve<IMyCustomService>();

        Assert.That(service.GetData(), Is.EqualTo("test-data"));
    }
}
```

> **Note:** Test fixtures that call `Service.InitializeContainer()` without inheriting from one of the _CMS.Tests_ base classes produce a compile-time warning, because container registrations can leak between fixtures.
