XUnit DI通过重写的启动文件(.net core)

时间:2016-12-27 16:59:30

标签: testing asp.net-core xunit

我已经构建了一个WebAPI,除了我在Postman上运行的测试之外,我想实现一些Integration / Unit测试。

现在我的业务逻辑很薄,大部分时间都是CRUD操作,所以我想从测试控制器开始。

我有一个基本的设置。存储库模式(接口),服务(业务逻辑)和控制器。 流程转到控制器(DI服务) - >服务(DI Repo) - >回购行动!

所以我做的是覆盖我的启动文件以更改为内存数据库,其余应该没问题(我会假设)添加服务,添加repos,现在我指向内存数据库,这很好我的基本测试。

namespace API.UnitTests
{    
    public class TestStartup : Startup
    {
        public TestStartup(IHostingEnvironment env)
            : base(env)
        {

        }

        public void ConfigureTestServices(IServiceCollection services)
        {
            base.ConfigureServices(services);
            //services.Replace<IService, IMockedService>();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            base.Configure(app, env, loggerFactory);
        }

        public override void SetUpDataBase(IServiceCollection services)
        {
            var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
            var connectionString = connectionStringBuilder.ToString();
            var connection = new SqliteConnection(connectionString);

            services
                .AddEntityFrameworkSqlite()
                .AddDbContext<ApplicationDbContext>(
                    options => options.UseSqlite(connection)
                );
        }
    }
}

我写了第一个测试,但DatasourceService不存在:

  

以下构造函数参数没有匹配的fixture数据:DatasourceService datasourceService

namespace API.UnitTests
{
    public class DatasourceControllerTest
    {
        private readonly DatasourceService _datasourceService; 

        public DatasourceControllerTest(DatasourceService datasourceService)
        { 
            _datasourceService = datasourceService;            
        }

        [Xunit.Theory,
        InlineData(1)]
        public void GetAll(int companyFk) {
            Assert.NotEmpty(_datasourceService.GetAll(companyFk));
        }
    }
}

我错过了什么?

2 个答案:

答案 0 :(得分:5)

您不能在测试类上使用依赖注入。你只能让xunit通过构造函数注入特殊的fixture(参见docs)。

对于集成测试,您希望使用TestServer包中的Microsoft.AspNetCore.TestHost类和单独的Startup.cs类(比继承imho更容易设置配置)。

public class TestStartup : Startup
{
    public TestStartup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureTestServices(IServiceCollection services)
    {
        services.Replace(ServiceDescriptor.Scoped<IService, MockedService>());
        services.AddEntityFrameworkSqlite()
            .AddDbContext<ApplicationDbContext>(
                options => options.UseSqlite(connection)
            );
    }

    public void Configure(IApplicationBuilder app)
    {
        // your usual registrations there
    }
}

在单元测试项目中,您需要创建TestServer的实例并执行测试。

public class DatasourceControllerTest
{
    private readonly TestServer _server; 
    private readonly HttpClient _client;

    public DatasourceControllerTest()
    {
        // Arrange
        _server = new TestServer(new WebHostBuilder()
            .UseStartup<TestStartup>());
        _client = _server.CreateClient();
    }

    [Xunit.Theory,
    InlineData(1)]
    public async Task GetAll(int companyFk) {
        // Act
        var response = await _client.GetAsync($"/api/datasource/{companyFk}");
        // expected result from rest service
        var expected = @"[{""data"":""value1"", ""data2"":""value2""}]";

        // Assert
        // This makes sure, you return a success http code back in case of 4xx status codes 
        // or exceptions (5xx codes) it throws an exception
        response.EnsureSuccessStatusCode();

        var resultString = await response.Content.ReadAsStringAsync();
        Assert.Equals(resultString, expectedString);
    }
}

现在,当您调用写入数据库的操作时,您还可以检查数据是否真正写入数据库:

[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
    // Act
    var response = await _client.DeleteAsync($"/api/datasource/{companyFk}");
    // expected result from rest service

    // Assert
    response.EnsureSuccessStatusCode();

    // now check if its really gone in the database. For this you need an instance 
    // of the in memory Sqlite DB. TestServer has a property Host, which is an IWebHost
    // and it has a property Services which is the IoC container

    var provider = _server.Host.Services;
    var dbContext = provider.GetRequiredService<ApplicationDbContext>();

    var result = await dbContext.YourTable.Where(entity => entity.Id == companyFk).Any();

    // if it was deleted, the query should result in false
    Assert.False(result);
}

答案 1 :(得分:1)

现在您可以在测试中使用 Xunit.DependencyInjection

namespace Your.Test.Project
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IDependency, DependencyClass>();
        }
    }
}

您的 DI 课程:

public interface IDependency
{
    int Value { get; }
}

internal class DependencyClass : IDependency
{
    public int Value => 1;
}

和 XUnit 测试:

public class MyAwesomeTests
{
    private readonly IDependency _d;

    public MyAwesomeTests(IDependency d) => _d = d;

    [Fact]
    public void AssertThatWeDoStuff()
    {
        Assert.Equal(1, _d.Value);
    }
}