如何在我的 XUnit 单元测试中使用 Microsoft.Extensions.Configuration.IConiguration [英] How to use Microsoft.Extensions.Configuration.IConiguration in my XUnit unit testing

查看:13
本文介绍了如何在我的 XUnit 单元测试中使用 Microsoft.Extensions.Configuration.IConiguration的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 Asp.net Core 2.0 应用程序中,我尝试对使用 Microsoft.Extensions.Configuration.IConfiguration 依赖注入的数据服务层(.Net 标准类库)进行单元测试.我正在使用 XUnit,但不知道如何从我的单元测试类中传递 IConfiguration.我尝试了以下实现并收到错误

In my Asp.net Core 2.0 application, I am trying to unit test my data service layer (.Net Standard Class Library) that uses the Microsoft.Extensions.Configuration.IConfiguration dependency injection. I am using XUnit and don't know how to pass IConfiguration from my unit test class. I tried the following implementation and getting the error

消息:以下构造函数参数没有匹配的夹具数据:IConfiguration 配置.

Message: The following constructor parameters did not have matching fixture data: IConfiguration configuration.

我对测试框架真的很陌生,甚至不知道是否可以像我在代码片段中尝试那样使用依赖注入.

I am really new to the testing frameworks and don't even know if dependency injection can be used as I am trying to do in my code snippet.

我的单元测试类如下

public class SqlRestaurantDataCLUnitTest
{
    private readonly IConfiguration configuration;
    public SqlRestaurantDataCLUnitTest(IConfiguration configuration)
    {
        this.configuration = configuration;
    }
    [Fact]
    public void AddTest()
    {
        var restaurantDataCL = new SqlRestaurantDataCL(configuration);
        var restaurant = new Restaurant
        {
            Name = "TestName",
            Cuisine = CuisineType.None
        };

        var result = restaurantDataCL.Add(restaurant);

        Assert.IsNotType(null, result.Id);    
    }
}

我的数据服务层如下

public class SqlRestaurantDataCL : IRestaurantDataCL
{
    private readonly IConfiguration configuration;
    public SqlRestaurantDataCL(IConfiguration configuration)
    {
        this.configuration = configuration;
    }
    public Restaurant Add(Restaurant restaurant)
    {
        using (var db = GetConnection())
        {
            string insertSql = @"INSERT INTO [dbo].[RESTAURANTS]([Cuisine], [Name]) 
                                OUTPUT INSERTED.*
                                VALUES (@Cuisine, @Name)";

            restaurant = db.QuerySingle<Restaurant>(insertSql, new
            {
                Cuisine = restaurant.Cuisine,
                Name = restaurant.Name
            });

            return restaurant;
        }
    }

    private IDbConnection GetConnection()
    {
        return new SqlConnection(configuration.GetSection(Connection.Name).Value.ToString());
    }
}

public class Connection
{
    public static string Name
    {
        get { return "ConnectionStrings: OdeToFood"; }
    }
}

推荐答案

单元测试有一个非常有用的暴露设计问题的习惯.在这种情况下,由于与框架关注点和静态关注点的紧密耦合,您做出了一些设计选择,这些选择被证明难以测试.

Unit tests have a very useful habit of exposing design issues. In this case you have made some design choices that prove difficult to test because of tight coupling to framework concerns as well as static concerns.

首先,看起来 SqlRestaurantDataCL 实际上依赖于一个连接工厂

First, it looks like SqlRestaurantDataCL actually depends on a connection factory

public interface IDbConnectionFactory {
    IDbConnection GetConnection();
}

这将根据建议重构数据实现以依赖于该抽象.

Which would refactor the data implementation as advised to depend on that abstraction.

public class SqlRestaurantDataCL : IRestaurantDataCL {
    private readonly IDbConnectionFactory factory;

    public SqlRestaurantDataCL(IDbConnectionFactory factory) {
        this.factory = factory;
    }
    public Restaurant Add(Restaurant restaurant) {
        using (var connection = factory.GetConnection()) {
            string insertSql = @"INSERT INTO [dbo].[RESTAURANTS]([Cuisine], [Name]) 
                                OUTPUT INSERTED.*
                                VALUES (@Cuisine, @Name)";

            restaurant = connection.QuerySingle<Restaurant>(insertSql, new {
                Cuisine = restaurant.Cuisine,
                Name = restaurant.Name
            });

            return restaurant;
        }
    }

    //...
}

假设是使用 Dapper 进行上述查询.

The assumption is that Dapper is being used to make the query above.

随着抽象依赖的引入,它们可以在隔离测试时根据需要进行模拟.

With the introduction of the abstracted dependencies, they can be mocked as needed when testing in isolation.

public class SqlRestaurantDataCLUnitTest {

    [Fact]
    public void AddTest() {
        //Arrange
        var connection = new Mock<IDbConnection>();
        var factory = new Mock<IDbConnectionFactory>();
        factory.Setup(_ => _.GetConnection()).Returns(connection.Object);

        //...setup the connection to behave as expected

        var restaurantDataCL = new SqlRestaurantDataCL(factory.Object);
        var restaurant = new Restaurant {
            Name = "TestName",
            Cuisine = CuisineType.None
        };

        //Act
        var result = restaurantDataCL.Add(restaurant);

        //Assert
        Assert.IsNotType(null, result.Id);
    }
}

现在,如果您打算实际接触真正的数据库,那么这不是一个隔离单元测试,而是一个集成测试,这将有不同的方法.

Now if you meant to actually touch the real database then this is not an isolation unit test but instead an integration test, that will have a different approach.

在生产代码中,工厂可以实现

In production code, the factory can be implemented

public class SqlDbConnectionFactory : IDbConnectionFactory {
    private readonly ConnectionSetings connectionSettings;

    SqlDbConnectionFactory(ConnectionSetings connectionSettings) {
        this.connectionSettings = connectionSettings;
    }

    public IDbConnection GetConnection() {
        return new SqlConnection(connectionSettings.Name));
    }
}

其中ConnectionSetings被定义为一个简单的POCO来存储连接字符串

Where ConnectionSetings is defined as a simple POCO to store the connection string

public class ConnectionSetings {
    public string Name { get; set; }
}

在组合根中,可以从配置中提取设置

In the composition root the settings can be extracted from configurations

IConfiguration Configuration; //this would have been set previously

public void ConfigureServices(IServiceCollection services) {
    //...

    var settings = Configuration
        .GetSection("ConnectionStrings:OdeToFood")
        .Get<ConnectionSetings>();

    //...verify settings (if needed)

    services.AddSingleton(settings);
    services.AddSingleton<IDbConnectionFactory,SqlDbConnectionFactory>();
    services.AddSingleton<IRestaurantDataCL,SqlRestaurantDataCL>();
    //Note: while singleton was used above, You can decide to use another scope
    //      if so desired.
}

实际上没有必要传递 IConfiguration,因为它更多的是一个框架问题,实际上只在启动时才相关.

There was really no need to be passing IConfiguration around as it is more of a framework concern that is really only relevant at start up.

这篇关于如何在我的 XUnit 单元测试中使用 Microsoft.Extensions.Configuration.IConiguration的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆