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

查看:60
本文介绍了如何在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天全站免登陆