XUnit 依赖注入

作者: 天天向上卡索 | 来源:发表于2018-10-08 17:13 被阅读10次

XUnit 依赖注入

Intro

现在的开发中越来越看重依赖注入的思想,微软的 Asp.Net Core 框架更是天然集成了依赖注入,那么在单元测试中如何使用依赖注入呢?

本文主要介绍如何通过 XUnit 来实现依赖注入, XUnit 主要借助 SharedContext 来共享一部分资源包括这些资源的创建以及释放。

Scoped

针对 Scoped 的对象可以借助 XUnit 中的 IClassFixture 来实现

  1. 定义自己的 Fixture,需要初始化的资源在构造方法里初始化,如果需要在测试结束的时候释放资源需要实现 IDisposable 接口
  2. 需要依赖注入的测试类实现接口 IClassFixture<Fixture>
  3. 在构造方法中注入实现的 Fixture 对象,并在构造方法中使用 Fixture 对象中暴露的公共成员

Singleton

针对 Singleton 的对象可以借助 XUnit 中的 ICollectionFixture 来实现

  1. 定义自己的 Fixture,需要初始化的资源在构造方法里初始化,如果需要在测试结束的时候释放资源需要实现 IDisposable 接口
  2. 创建 CollectionDefinition,实现接口 ICollectionFixture<Fixture>,并添加一个 [CollectionDefinition("CollectionName")] Attribute,CollectionName 需要在整个测试中唯一,不能出现重复的 CollectionName
  3. 在需要注入的测试类中添加 [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 测试的测试为例

  1. 自定义 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;
            }
        }
    
  2. 自定义Collection

        [CollectionDefinition("TestCollection")]
        public class TestCollection : ICollectionFixture<TestStartupFixture>
        {
        }
    
  3. 自定义一个 TestBase

        [Collection("TestCollection")]
        public class ControllerTestBase
        {
            protected readonly HttpClient Client;
            protected readonly IServiceProvider ServiceProvider;
    
            public ControllerTestBase(TestStartupFixture fixture)
            {
                Client = fixture.Client;
                ServiceProvider = fixture.Services;
            }
        }
    
  4. 需要依赖注入的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);
        }
    }

Reference

Contact

如果您有什么问题,欢迎随时联系我

Contact me: weihanli@outlook.com

相关文章

  • XUnit 依赖注入

    XUnit 依赖注入 Intro 现在的开发中越来越看重依赖注入的思想,微软的 Asp.Net Core 框架更是...

  • 在 Xunit 中使用依赖注入

    在 Xunit 中使用依赖注入 Intro 之前写过一篇 xunit 的依赖注入相关的文章,但是实际使用起来不是那...

  • 更优雅的在 Xunit 中使用依赖注入

    Xunit.DependencyInjection 7.0 发布了 Intro 上次我们已经介绍过一次大师的 Xu...

  • 开源项目的依赖注入

    开源项目的依赖注入 依赖注入概念 依赖注入(DI:Dependency Injection): 依赖注入方式: B...

  • 资料收集

    依赖注入 AngularJs依赖注入的研究 (已读) 依赖注入(已读)

  • Dagger2常用注解诠释

    依赖注入 控制反转(IoC)与依赖注入(DI)浅谈依赖注入理解依赖注入(IOC)和学习Unity Gradle配置...

  • Day62 Spring 依赖注入源码解析

    手动注入自动注入 依赖注入,set注入,构造注入 依赖注入: Key依据 byType byName constr...

  • Dagger2 源码分析

    Dagger简单介绍 Dagger2涉及到依赖注入,有关依赖注入的请到理解依赖注入 通过注解方式实现依赖注入分为两...

  • 依赖注入(转)

    依赖注入(转) 原文地址:依赖注入原理

  • Spring学习之依赖注入

    Spring学习之依赖注入 依赖注入的基本概念 依赖注入(Dependecy Injection),也称为IoC(...

网友评论

    本文标题:XUnit 依赖注入

    本文链接:https://www.haomeiwen.com/subject/orujaftx.html