NUnit 测试项目的 .NET Core 容器定义

.NET Core container definition for NUnit test project

我是 .NET Core 的新手。如何在 NUnit class 库项目中定义 DI 容器?

我知道是通过IServiceCollection完成的,但是由于没有Startup方法,所以我不知道从哪里得到实现这个接口的实例。

我还希望能够从其他 class 库(作为测试对象)加载定义。这应该更简单,因为我可以在那个 class 库中创建一个静态方法,其中一个参数是 IServiceCollection,但同样,我如何获得它?

一个附带的问题是:我假设某些接口可以出于测试目的而被模拟,但是我如何才能替换已经使用 IServiceCollection 的方法创建的映射,例如 AddSingletonAddTransient?

有一个Remove方法,但没有记录。

通常您不想为您的测试创建一个 DI 容器,但正如您所意识到的,您想要模拟它们。因此,例如,如果这是您要测试的 class:

public class UserService
{
    private readonly IUserDatabase _userDatabase;

    public UserService(IUserDatabase userDatabase)
    {
        _userDatabase = userDatabase;
    }

    public bool DoesUserExist(int userId)
    {
        return _userDatabase.UserExists(userId);
    }
}

这是所用接口的定义:

public interface IUserDatabase
{
    bool UserExists(int userId);
}

在我们的测试中,我们可以将接口模拟为 return 我们想要用于测试的特定值:

[TestClass]
public class UserServiceTests
{
    [TestMethod]
    public void DoesUserExist_ForValidUserId_ReturnsTrue()
    {
        var fakeUserId = 123;

        var mockUserDatabase = new Mock<IUserDatabase>();
        mockUserDatabase.Setup(udb => udb.UserExists(fakeUserId)).Returns(true);

        var userService = new UserService(mockUserDatabase.Object);

        var result = userService.DoesUserExist(fakeUserId);

        Assert.IsTrue(result);
        mockUserDatabase.VerifyAll();
    }
}

所以在这个测试中,我们使用 Moq 创建了我们界面的模拟。我们不需要使用 DI 容器,因为我们在创建我们正在测试的 class 的控制器中。 DI 容器在生产中有更多用途,因为它使应用程序能够创建它需要的任何依赖项,而无需您的代码调用 new - 如果您尝试对 class 进行单元测试,这是一个大问题es.

.VerifyAll() 方法检查在模拟对象上设置的任何方法,在本例中我们设置对 UserExists 的调用,是否实际被调用。

一般来说,有很多关于如何使用 Moq 和模拟接口的示例。 Moq 快速入门指南是 here.

IServiceCollection是由ServiceCollecionclass实现的。因此,如果您想为集成测试执行此操作,则可以使用 ServiceCollection class 创建您自己的 ServiceProvider.

var services = new ServiceCollection();

services.AddTransient<IMyInterface, MyClass>();
services.AddScoped<IMyScopedInteface, MyScopedClass>();
...

var serviceProvider = sc.BuildServiceProvider();

您现在可以在测试中使用 serviceProvider 实例来获取 classes:

var myClass = serviceProvider.GetService<IMyInterface>();

如果您想模拟某些接口而不是使用真实的接口,那么您可以添加一个模拟而不是将真实的 class/interface 添加到服务集合中:

mockInterface = new Mock<IMyInterface>();

sc.AddScoped<IMyInterface>(factory => mockInterface.Object);