单元测试时,您应该提供所有您明确测试的类的所有依赖关系。 即依赖注入;没有服务构建它自己的依赖关系,而是依赖外部组件来提供它们。当你在一个依赖注入容器之外,并且在一个单元测试中你正在手动创建你正在测试的类时,它的你的责任提供依赖关系。
实际上,这意味着您要么向构造函数提供模拟对象或实际对象。例如,您可能想要提供一个真实的记录器,但没有目标,一个连接内存数据库的真实数据库上下文或一些模拟服务。
假设在这个例子中,该服务正在测试这个样子的:
public class ExampleService
{
public ExampleService(ILogger<ExampleService> logger,
MyDbContext databaseContext,
UtilityService utilityService)
{
// …
}
// …
}
所以为了测试ExampleService
,我们需要提供这三个对象。在这种情况下,我们会做以下每个:
ILogger<ExampleService>
- 我们将用一个真实的记录,没有任何连接的目标。因此,任何对记录器的调用都能正常工作,而我们不需要提供一些模拟,但我们不需要测试日志输出,因此我们不需要实际的目标。
MyDbContext
- 在这里,我们将使用真实数据库上下文与附加的内存数据库
UtilityService
- 为此,我们将创建一个模拟,它只是在我们想要测试的方法内设置我们需要的实用方法。
所以单元测试看起来是这样的:
[Fact]
public async Task TestExampleMethod()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// using Moq as the mocking library
var utilityServiceMock = new Mock<UtilityService>();
utilityServiceMock.Setup(u => u.GetRandomNumber()).Returns(4);
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<Customer>().Add(new Customer()
{
Id = 2,
Name = "Foo bar"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new ExampleService(logger, db, utilityServiceMock.Object);
// act
var result = service.DoSomethingWithCustomer(2);
// assert
Assert.NotNull(result);
Assert.Equal(2, result.CustomerId);
Assert.Equal("Foo bar", result.CustomerName);
Assert.Equal(4, result.SomeRandomNumber);
}
}
在您的具体Cancel
情况下,要避免使用你是不是目前正在测试该服务的任何方法。所以如果你想测试Cancel
,你应该从你的服务调用的唯一方法是Cancel
。测试可能看起来像这样(只是猜测这里的依赖关系):
[Fact]
public async Task Cancel_StatusShouldBeN()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<SomeItem>().Add(new SomeItem()
{
Id = 5,
Status = "Not N"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new YourService(logger, db);
// act
var result = service.Cancel(5);
// assert
Assert.Equal(1, result);
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
var item = db.Set<SomeItem>().Find(5);
Assert.Equal(5, item.Id);
Assert.Equal("n", item.Status);
}
}
Btw。请注意,我始终打开一个新的数据库上下文,以避免从缓存的实体获取结果。通过打开一个新的上下文,我可以验证这些更改实际上是否完全将其导入到数据库中。
对于这种测试,您不应该使用术语“单元测试”。这是一个集成测试,因为对于测试,您依赖于具体的'DbContext'实现。单元测试可以在没有外部依赖的情况下进行测试(即通过模拟接口)。这是当你在你的服务中使用EF Core时的缺点之一,而不是将其抽象到存储库或通过CQRS – Tseng