Intro
现在的开发中越来越看重依赖注入的思想, 微软的 ASP.NET Core 框架更是天然集成了依赖注入, 那么在单元测试中如何使用依赖注入呢?
本文主要介绍如何通过 XUnit 来实现依赖注入, XUnit 主要借助 SharedContext 来共享一部分资源包括这些资源的创建以及释放.
Scoped
针对 Scoped 的对象可以借助 XUnit 中的 IClassFixture 来实现
定义自己的 Fixture, 需要初始化的资源在构造方法里初始化, 如果需要在测试结束的时候释放资源需要实现 IDisposable 接口
需要依赖注入的测试类实现接口
IClassFixture<Fixture>
在构造方法中注入实现的 Fixture 对象, 并在构造方法中使用 Fixture 对象中暴露的公共成员
Singleton
针对 Singleton 的对象可以借助 XUnit 中的 ICollectionFixture 来实现
定义自己的 Fixture, 需要初始化的资源在构造方法里初始化, 如果需要在测试结束的时候释放资源需要实现 IDisposable 接口
创建 CollectionDefinition, 实现接口
ICollectionFixture<Fixture>
, 并添加一个
[CollectionDefinition("CollectionName")]
Attribute,CollectionName 需要在整个测试中唯一, 不能出现重复的 CollectionName
在需要注入的测试类中添加
[Collection("CollectionName")]
Attribute, 然后在构造方法中注入对应的 Fixture
Tips
如果有多个类需要依赖注入, 可以通过一个基类来做, 这样就只需要一个基类上添加
[Collection("CollectionName")]
Attribute, 其他类只需要集成这个基类就可以了
- Samples
- Scoped Sample
这里直接以 XUnit 的示例为例:
- public class DatabaseFixture : IDisposable
- {
- public DatabaseFixture()
- {
- Db = new SqlConnection("MyConnectionString");
- // ... initialize data in the test database ...
- }
- public void Dispose()
- {
- // ... clean up test data from the database ...
- }
- public SqlConnection Db { get; private set; }
- }
- public class MyDatabaseTests : IClassFixture<DatabaseFixture>
- {
- DatabaseFixture fixture;
- public MyDatabaseTests(DatabaseFixture fixture)
- {
- this.fixture = fixture;
- }
- [Fact]
- public async Task GetTest()
- {
- // ... write tests, using fixture.Db to get access to the SQL Server ...
- // ... 在这里使用注入 的 DatabaseFixture
- }
- }
- Singleton Sample
这里以一个对 Controller 测试的测试为例
自定义 Fixture
- /// <summary>
- /// A test fixture which hosts the target project (project we wish to test) in an in-memory server.
- /// </summary>
- public class TestStartupFixture : IDisposable
- {
- private readonly IwebHost _server;
- public IServiceProvider Services { get; }
- public HttpClient Client { get; }
- public string ServiceBaseUrl { get; }
- public TestStartupFixture()
- {
- var builder = WebHost.CreateDefaultBuilder()
- .UseUrls($"http://localhost:{GetRandomPort()}")
- .UseStartup<TestStartup>();
- _server = builder.Build();
- _server.Start();
- var url = _server.ServerFeatures.Get<IServerAddressesFeature>().Addresses.First();
- Services = _server.Services;
- ServiceBaseUrl = $"{url}/API/";
- Client = new HttpClient()
- {
- BaseAddress = new Uri(ServiceBaseUrl)
- };
- Initialize();
- }
- /// <summary>
- /// TestDataInitialize
- /// </summary>
- private void Initialize()
- {
- // ...
- }
- public void Dispose()
- {
- Client.Dispose();
- _server.Dispose();
- }
- private static readonly Random Random = new Random();
- private static int GetRandomPort()
- {
- var activePorts = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners().Select(_ => _.Port).ToList();
- var randomPort = Random.Next(10000, 65535);
- while (activePorts.Contains(randomPort))
- {
- randomPort = Random.Next(10000, 65535);
- }
- return randomPort;
- }
- }
自定义 Collection
- [CollectionDefinition("TestCollection")]
- public class TestCollection : ICollectionFixture<TestStartupFixture>
- {
- }
自定义一个 TestBase
- [Collection("TestCollection")]
- public class ControllerTestBase
- {
- protected readonly HttpClient Client;
- protected readonly IServiceProvider ServiceProvider;
- public ControllerTestBase(TestStartupFixture fixture)
- {
- Client = fixture.Client;
- ServiceProvider = fixture.Services;
- }
- }
需要依赖注入的 Test 类写法
- public class AttendancesTest : ControllerTestBase
- {
- public AttendancesTest(TestStartupFixture fixture) : base(fixture)
- {
- }
- [Fact]
- public async Task GetAttendances()
- {
- var response = await Client.GetAsync("attendances");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- response = await Client.GetAsync("attendances?type=1");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
- }
来源: https://www.cnblogs.com/weihanli/p/dependency-injection-in-xunit.html