使用Postgres的ASP.NET中的UnitTest

时间:2018-04-15 17:22:11

标签: asp.net unit-testing asp.net-core

我编写了一些与PostgreSQL一起使用的已创建系统的测试。我在解决方案中创建了类型为Class Library(.NET Core)的新项目。然后,我创建了一个类,它测试类DocumentRepository。但是在DocumentRepository的构造函数中使用IConfiguration(用于连接数据库),而这个IConfiguration我不能在测试类中调用。我如何在UnitTest中模仿与数据库的连接?

这个课,我想测试

public class DocumentsRepository : IRepository<Documents>
    {
        private string connectionString;

        public DocumentsRepository(IConfiguration configuration, string login, string password)
        {
            connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
            connectionString = connectionString.Replace("username", login);
            connectionString = connectionString.Replace("userpassword", password);
        }

        internal IDbConnection Connection
        {
            get
            {
                return new NpgsqlConnection(connectionString);
            }
        }

        public void Add(Documents item)
        {
            using (IDbConnection dbConnection = Connection)
            {
                dbConnection.Open();
                dbConnection.Execute("SELECT addrecuserdocuments(@DocumentName,@Contents,@DocumentIntroNumber)", item);
            }
        }

此处测试,我尝试使用

using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebApplication4.Controllers;
using WebApplication4.Entites;
using WebApplication4.ViewModels;
using Xunit;

namespace TestsApp
{
    public class UserControllerTest
    {
        private IConfiguration configuration;
        private string connectionString;

        [Fact]
        public async Task IndexUsers()
        {
            connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
            var aCon = new AccountController(configuration);
            var uCon = new UserController(configuration);

            LoginModel model = new LoginModel
            {
                Login = "postgres",
                Password = "111"
            };

            aCon.Authorization(model);

            var result = uCon.Index();

            var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
            var persons = okResult.Value.Should().BeAssignableTo<IEnumerable<Documents>>().Subject;

            persons.Count().Should().Be(7);
        }
    }
}

测试显示我的错误

var result = uCon.Index();

让我得到NullReferenceException。

我如何解决这个问题?

1 个答案:

答案 0 :(得分:2)

首先,您不是单元测试,而是集成测试。一旦你在混合中得到类似数据库连接的东西,单元测试就完全可以了。如果您的目标是为存储库类编写单元测试,那么您应该模拟数据存储。

其次,你不应该注入IConfiguration,如果你需要配置中的一些数据,比如连接字符串,你应该将它绑定到一个强类型的类,然后注入它:

services.Configure<MyConnectionStringsClass>(Configuration.GetSection("ConnectionStrings"));

然后,注入IOptionsSnapshot<MyConnectionStringsClass>

第三,无论如何,你真的不应该这样处理它。如果您的存储库依赖于IDbConnection,那么您应该注入到您的存储库中。在Startup.cs中:

services.AddScoped(p => new NpgsqlConnection(Configuration.GetConnectionString("Foo"));

然后,在您的repo构造函数中接受NpgsqlConnection并将其设置为私有字段。

第四,如果你坚持继续你现在的方式,你绝对不应该在你的Connection财产上有新闻NpgsqlConnection。这意味着每次访问此属性时都会获得一个新实例。相反,您应该将其定义为简单{ get; private set; },并将其设置在repo的构造函数中。

第五,您不应该将using与以任何一种方式定义的属性一起使用,因为它将在您第一次执行后处理,使所有后续查询失败并显示ObjectDisposedException。如果您要在课堂上重新修改,那么您的课程需要实施IDisposable,您应该使用Dispose方法处理您的连接。 FWIW,如果你将所有依赖项(包括你的连接)注入你的类,你不需要实现IDisposable,因为它不需要处理所需的类 - 使用依赖注入的另一个重要原因顺便说一下。

最后,为了回答您的主要问题,您应该使用TestServer。在创建TestServer时,您将Startup类作为类型参数传递给您,因此您最终会获得实际应用的真实副本,以及所有相应的服务等。然后,您可以发出HTTP请求,就像使用HttpClient来测试控制器操作等。但是,这仅适用于集成测试,这是唯一一次真正拥有PostreSQL数据库的方法。