如何在我的 XUnit 单元测试中使用 Microsoft.Extensions.Configuration.IConiguration
How to use Microsoft.Extensions.Configuration.IConiguration in my XUnit unit testing
在我的 Asp.net Core 2.0 应用程序中,我正在尝试对使用 Microsoft.Extensions.Configuration.IConfiguration 的数据服务层(.Net Standard Class 库)进行单元测试 依赖注入。
我正在使用 XUnit,但不知道如何通过我的单元测试 IConfiguration
class。我尝试了以下实现并收到错误
Message: The following constructor parameters did not have matching fixture data: IConfiguration configuration.
我真的是测试框架的新手,甚至不知道是否可以像我在代码片段中尝试的那样使用依赖注入。
我的单元测试class如下
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"; }
}
}
单元测试有一个非常有用的暴露设计问题的习惯。在这种情况下,由于与框架问题和静态问题的紧密耦合,您做出了一些难以测试的设计选择。
首先,看起来 SqlRestaurantDataCL
实际上依赖于一个连接工厂
public interface IDbConnectionFactory {
IDbConnection GetConnection();
}
这将根据建议重构数据实现以依赖于该抽象。
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 进行上述查询。
随着抽象依赖项的引入,在隔离测试时可以根据需要模拟它们。
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);
}
}
现在,如果您打算实际接触真实数据库,那么这不是隔离单元测试而是集成测试,它将采用不同的方法。
在生产代码中,工厂可以实现
public class SqlDbConnectionFactory : IDbConnectionFactory {
private readonly ConnectionSetings connectionSettings;
SqlDbConnectionFactory(ConnectionSetings connectionSettings) {
this.connectionSettings = connectionSettings;
}
public IDbConnection GetConnection() {
return new SqlConnection(connectionSettings.Name));
}
}
其中ConnectionSetings
定义为一个简单的POCO来存储连接字符串
public class ConnectionSetings {
public string Name { get; set; }
}
在组合根中,可以从配置中提取设置
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
,因为它更像是一个框架问题,实际上只在启动时相关。
在我的 Asp.net Core 2.0 应用程序中,我正在尝试对使用 Microsoft.Extensions.Configuration.IConfiguration 的数据服务层(.Net Standard Class 库)进行单元测试 依赖注入。
我正在使用 XUnit,但不知道如何通过我的单元测试 IConfiguration
class。我尝试了以下实现并收到错误
Message: The following constructor parameters did not have matching fixture data: IConfiguration configuration.
我真的是测试框架的新手,甚至不知道是否可以像我在代码片段中尝试的那样使用依赖注入。
我的单元测试class如下
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"; }
}
}
单元测试有一个非常有用的暴露设计问题的习惯。在这种情况下,由于与框架问题和静态问题的紧密耦合,您做出了一些难以测试的设计选择。
首先,看起来 SqlRestaurantDataCL
实际上依赖于一个连接工厂
public interface IDbConnectionFactory {
IDbConnection GetConnection();
}
这将根据建议重构数据实现以依赖于该抽象。
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 进行上述查询。
随着抽象依赖项的引入,在隔离测试时可以根据需要模拟它们。
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);
}
}
现在,如果您打算实际接触真实数据库,那么这不是隔离单元测试而是集成测试,它将采用不同的方法。
在生产代码中,工厂可以实现
public class SqlDbConnectionFactory : IDbConnectionFactory {
private readonly ConnectionSetings connectionSettings;
SqlDbConnectionFactory(ConnectionSetings connectionSettings) {
this.connectionSettings = connectionSettings;
}
public IDbConnection GetConnection() {
return new SqlConnection(connectionSettings.Name));
}
}
其中ConnectionSetings
定义为一个简单的POCO来存储连接字符串
public class ConnectionSetings {
public string Name { get; set; }
}
在组合根中,可以从配置中提取设置
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
,因为它更像是一个框架问题,实际上只在启动时相关。