我们提供安全,免费的手游软件下载!
上一篇我们创建了一个
Sample.Api
项目和
Sample.Repository
,并且带大家熟悉了一下
Moq
的概念,这一章我们来实战一下在
xUnit
项目使用依赖注入。
Xunit.DependencyInjection
是一个用于
xUnit
测试框架的扩展库,它提供了依赖注入的功能,使得在编写单元测试时可以更方便地进行依赖注入。通过使用
Xunit.DependencyInjection
,可以在
xUnit
测试中使用依赖注入容器(比如
Microsoft.Extensions.DependencyInjection
)来管理测试中所需的各种依赖关系,包括服务、日志、配置等等。
我们用
Xunit.DependencyInjection
对上一章的
Sample.Repository
进行单元测试。
Nuget
包安装项目依赖
PM> NuGet\Install-Package Xunit.DependencyInjection -Version 9.1.0
public class StaffRepositoryTest
{
[Fact]
public void DependencyInject_WhenCalled_ReturnTrue()
{
Assert.True(true);
}
}
运行测试 先看一下
从这可以得出一个结论 如果安装了
Xunit.DependencyInjection
的xUnit
单元测试项目启动时会检测是否有默认的Startup
类
如果你安装了
Xunit.DependencyInjection
但是还没有准备好在项目中使用也可以在
csproj
中禁用
false
再测试一下
可以看到我们添加的配置生效了
在我们的测试项目中新建
Startup.cs
public class Startup
{
}
在
.Net 6
之前我们不就是用这个来配置项目的依赖和管道吗,其实这个位置也一样用它来对我们项目的依赖和服务做一些基础配置,使用配置单元测试的
Startup
其实和配置我们的
Asp.Net Core
的启动配置是一样的
CreateHostBuilder
方法用于创建应用程序的主机构建器(
HostBuilder
)。在这个方法中,您可以配置主机的各种参数、服务、日志、环境等。这个方法通常用于配置主机构建器的各种属性,以便在应用程序启动时使用。
public IHostBuilder CreateHostBuilder([AssemblyName assemblyName]) { }
ConfigureHost
方法用于配置主机构建器。在这个方法中,您可以对主机进行一些自定义的配置,比如设置环境、使用特定的配置源等
public void ConfigureHost(IHostBuilder hostBuilder) { }
ConfigureServices
方法用于配置依赖注入容器(
ServiceCollection
)。在这个方法中,您可以注册应用程序所需的各种服务、中间件、日志、数据库上下文等等。这个方法通常用于配置应用程序的依赖注入服务。
ConfigureServices
中配置的服务可以在
Configure
方法中指定。如果已经配置的服务在 Configure 方法的参数中可用,它们将会被注入
public void Configure()
{
}
Sample.Repository
接下来对我们的仓储层进行单元测试
已知我们的仓储层已经有注入的扩展方法
public static IServiceCollection AddEFCoreInMemoryAndRepository(this IServiceCollection services)
{
services.AddScoped();
services.AddDbContext(options => options.UseInMemoryDatabase("sample").EnableSensitiveDataLogging(), ServiceLifetime.Scoped);
return services;
}
所以我们只需要在单元测试项目的
Startup
的
ConfigureServices
注入即可。
对我们的
Sample.Repository
添加项目引用,然后进行依赖注册
public void ConfigureServices(IServiceCollection services, HostBuilderContext context)
{
services.AddEFCoreInMemoryAndRepository();
}
好了接下来编写单元测试
Case
依赖项获取:
public class StaffRepositoryTest
{
private readonly IStaffRepository _staffRepository;
public StaffRepositoryTest(IStaffRepository staffRepository)
{
_staffRepository = staffRepository;
}
}
在测试类中使用依赖注入和我们正常获取依赖是一样的都是通过构造函数的形式
public class StaffRepositoryTest
{
private readonly IStaffRepository _staffRepository;
public StaffRepositoryTest(IStaffRepository staffRepository)
{
_staffRepository = staffRepository;
}
//[Fact]
//public void DependencyInject_WhenCalled_ReturnTrue()
//{
// Assert.True(true);
//}
[Fact]
public async Task AddStaffAsync_WhenCalled_ShouldAddStaffToDatabase()
{
// Arrange
var staff = new Staff { Name = "zhangsan", Email = "zhangsan@163.com" };
// Act
await _staffRepository.AddStaffAsync(staff, CancellationToken.None);
// Assert
var retrievedStaff = await _staffRepository.GetStaffByIdAsync(staff.Id, CancellationToken.None);
Assert.NotNull(retrievedStaff); // 确保 Staff 已成功添加到数据库
Assert.Equal("zhangsan", retrievedStaff.Name); // 检查名称是否正确
}
[Fact]
public async Task DeleteStaffAsync_WhenCalled_ShouldDeleteStaffFromDatabase()
{
var staff = new Staff { Name = "John", Email = "john@example.com" };
await _staffRepository.AddStaffAsync(staff, CancellationToken.None); // 先添加一个 Staff
// Act
await _staffRepository.DeleteStaffAsync(staff.Id, CancellationToken.None); // 删除该 Staff
// Assert
var retrievedStaff = await _staffRepository.GetStaffByIdAsync(staff.Id, CancellationToken.None); // 尝试获取已删除的 Staff
Assert.Null(retrievedStaff); // 确保已经删除
}
[Fact]
public async Task UpdateStaffAsync_WhenCalled_ShouldUpdateStaffInDatabase()
{
// Arrange
var staff = new Staff { Name = "John", Email = "john@example.com" };
await _staffRepository.AddStaffAsync(staff, CancellationToken.None); // 先添加一个 Staff
// Act
staff.Name = "Updated Name";
await _staffRepository.UpdateStaffAsync(staff, CancellationToken.None); // 更新 Staff
// Assert
var updatedStaff = await _staffRepository.GetStaffByIdAsync(staff.Id, CancellationToken.None); // 获取已更新的 Staff
Assert.Equal("Updated Name", updatedStaff?.Name); // 确保 Staff 已更新
}
[Fact]
public async Task GetStaffByIdAsync_WhenCalledWithValidId_ShouldReturnStaffFromDatabase()
{
// Arrange
var staff = new Staff { Name = "John", Email = "john@example.com" };
await _staffRepository.AddStaffAsync(staff, CancellationToken.None); // 先添加一个 Staff
// Act
var retrievedStaff = await _staffRepository.GetStaffByIdAsync(staff.Id, CancellationToken.None); // 获取 Staff
// Assert
Assert.NotNull(retrievedStaff); // 确保成功获取 Staff
}
[Fact]
public async Task GetAllStaffAsync_WhenCalled_ShouldReturnAllStaffFromDatabase()
{
// Arrange
var staff1 = new Staff { Name = "John", Email = "john@example.com" };
var staff2 = new Staff { Name = "Alice", Email = "alice@example.com" };
await _staffRepository.AddStaffAsync(staff1, CancellationToken.None); // 先添加 Staff1
await _staffRepository.AddStaffAsync(staff2, CancellationToken.None); // 再添加 Staff2
// Act
var allStaff = await _staffRepository.GetAllStaffAsync(CancellationToken.None); // 获取所有 Staff
// Assert
List addStaffs = [staff1, staff2];
Assert.True(addStaffs.All(_ => allStaff.Any(x => x.Id == _.Id))); // 确保成功获取所有 Staff
}
}
Run Tests
可以看到单元测试已经都成功了,是不是很简单呢。
之前的示例不使用
xUnit.DependencyInjection
我们用
ITestOutputHelper
通过构造函数构造,现在是用
ITestOutputHelperAccessor
public class DependencyInjectionTest
{
private readonly ITestOutputHelperAccessor _testOutputHelperAccessor;
public DependencyInjectionTest(ITestOutputHelperAccessor testOutputHelperAccessor)
{
_testOutputHelperAccessor = testOutputHelperAccessor;
}
[Fact]
public void TestOutPut_Console()
{
_testOutputHelperAccessor.Output?.WriteLine("测试ITestOutputHelperAccessor");
Assert.True(true);
}
}
OutPut:
Nuget
安装
PM> NuGet\Install-Package Xunit.DependencyInjection.Logging -Version 9.0.0
ConfigureServices
配置依赖
public void ConfigureServices(IServiceCollection services)
=> services.AddLogging(lb => lb.AddXunitOutput());
使用:
public class DependencyInjectionTest
{
private readonly ILogger _logger;
public DependencyInjectionTest(ILogger logger)
{
_logger = logger;
}
[Fact]
public void Test()
{
_logger.LogDebug("LogDebug");
_logger.LogInformation("LogInformation");
_logger.LogError("LogError");
}
}
OutPut:
标准输出:
[2024-04-12 16:00:24Z] info: dotNetParadise.DependencyInjection.DependencyInjectionTest[0]
LogInformation
[2024-04-12 16:00:24Z] fail: dotNetParadise.DependencyInjection.DependencyInjectionTest[0]
LogError
通过
ConfigureServices
设置
EnvironmentName
和使用
IConfiguration
public void ConfigureServices(HostBuilderContext context)
{
context.HostingEnvironment.EnvironmentName = "test";
//使用配置
context.Configuration.GetChildren();
}
也可以使用
Startup
下的
ConfigureHost
设置
public class Startup
{
public void ConfigureHost(IHostBuilder hostBuilder) =>
hostBuilder
.ConfigureServices((context, services) => { context.XXXX });
}
在 ConfigureHost 下可以对
.Net
IHostBuilder
进行配置,可以对
IConfiguration
,
IServiceCollection
,
Log
等跟
Asp.Net Core
使用一致。
xUnit.DependencyInjection
也可以对
Asp.Net Core
项目进行集成测试
安装
Microsoft.AspNetCore.TestHost
PM> NuGet\Install-Package Microsoft.AspNetCore.TestHost -Version 9.0.0-preview.3.24172.13
public void ConfigureHost(IHostBuilder hostBuilder) =>
hostBuilder.ConfigureWebHost[Defaults](webHostBuilder => webHostBuilder
.UseTestServer(options => options.PreserveExecutionContext = true)
.UseStartup());
可以参考 xUnit 的官网实现,其实有更优雅的实现集成测试的方案,
xUnit.DependencyInject
的集成测试方案仅做参考集合,在后面章节笔者会对集成测试做详细的介绍。
希望本文对您在使用
Xunit.DependencyInjection
进行依赖注入和编写单元测试时有所帮助。通过本文的介绍,您可以更加灵活地管理测试项目中的依赖关系,提高测试代码的可维护性和可测试性
?欢迎关注笔者公众号一起学习交流,获取更多有用的知识~
热门资讯