web-dev-qa-db-ja.com

ASP.NET Core Web APIとEF Coreの統合テスト時に依存関係を再構成する

このチュートリアルに従っています
Entity Framework CoreおよびSQL Serverとの統合テスト

私のコードはこのようになります

統合テストクラス

public class ControllerRequestsShould : IDisposable
{
    private readonly TestServer _server;
    private readonly HttpClient _client;
    private readonly YourContext _context;

    public ControllerRequestsShould()
    {
        // Arrange
        var serviceProvider = new ServiceCollection()
            .AddEntityFrameworkSqlServer()
            .BuildServiceProvider();

        var builder = new DbContextOptionsBuilder<YourContext>();

        builder.UseSqlServer($"Server=(localdb)\\mssqllocaldb;Database=your_db_{Guid.NewGuid()};Trusted_Connection=True;MultipleActiveResultSets=true")
            .UseInternalServiceProvider(serviceProvider);

        _context = new YourContext(builder.Options);
        _context.Database.Migrate();

        _server = new TestServer(new WebHostBuilder()
            .UseStartup<Startup>()
            .UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")));
        _client = _server.CreateClient();
    }

    [Fact]
    public async Task ReturnListOfObjectDtos()
    {
        // Arrange database data
        _context.ObjectDbSet.Add(new ObjectEntity{ Id = 1, Code = "PTF0001", Name = "Portfolio One" });
        _context.ObjectDbSet.Add(new ObjectEntity{ Id = 2, Code = "PTF0002", Name = "Portfolio Two" });

        // Act
        var response = await _client.GetAsync("/api/route");
        response.EnsureSuccessStatusCode();


        // Assert
        var result = Assert.IsType<OkResult>(response);            
    }

    public void Dispose()
    {
        _context.Dispose();
    }

私が理解しているように、.UseStartUpメソッドはTestServerが私のスタートアップクラスを使用することを保証します

私が抱えている問題は、私のActステートメントがヒットしたときに

var response = await _client.GetAsync("/api/route");

スタートアップクラスで、接続文字列がnullであるというエラーが発生します。私の問題の理解は、コントローラーがクライアントからヒットされると、データリポジトリを挿入し、それがデータベースコンテキストを挿入するということです。

テストで作成されたコンテキストを使用するように、new WebHostBuilderセクションの一部としてサービスを構成する必要があると思います。しかし、これを行う方法がわかりません。

Startup.csのConfigureServicesメソッド

        public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services
        services.AddMvc(setupAction =>
        {
            setupAction.ReturnHttpNotAcceptable = true;
            setupAction.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
            setupAction.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
        });

        // Db context configuration
        var connectionString = Configuration["ConnectionStrings:YourConnectionString"];
        services.AddDbContext<YourContext>(options => options.UseSqlServer(connectionString));

        // Register services for dependency injection
        services.AddScoped<IYourRepository, YourRepository>();
    }
19
GreenyMcDuff

2つのオプションがあります。

1. WebHostBuilder.ConfigureServicesを使用します

WebHostBuilder.ConfigureServicesWebHostBuilder.UseStartup<T>と組み合わせて使用​​して、WebアプリケーションのDI登録をオーバーライドおよびモックします。

_server = new TestServer(new WebHostBuilder()
    .ConfigureServices(services =>
    {
        services.AddScoped<IFooService, MockService>();
    })
    .UseStartup<Startup>()
);

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //use TryAdd to support mocking IFooService
        services.TryAddTransient<IFooService, FooService>();
    }
}

ここで重要なのは、元のTryAddクラス内でStartupメソッドを使用することです。カスタムWebHostBuilder.ConfigureServicesは、元のStartupの前にbeforeと呼ばれるため、モックは元のサービスの前に登録されます。 TryAddは、同じインターフェースがすでに登録されている場合は何もしません。したがって、実際のサービスには触れられません。

詳細: ASP.NET Coreアプリの統合テストの実行

2.継承/新しいスタートアップクラス

ASP.NET Core DIを再構成するためのTestStartupクラスを作成します。 Startupから継承して、必要なメソッドのみをオーバーライドできます。

public class TestStartup : Startup
{
    public TestStartup(IHostingEnvironment env) : base(env) { }

    public override void ConfigureServices(IServiceCollection services)
    {
        //mock DbContext and any other dependencies here
    }
}

あるいは、TestStartupを最初から作成して、テストをよりクリーンに保つことができます。

そして、それをUseStartupで指定して、テストサーバーを実行します。

_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());

これは完全に大きな例です: インメモリデータベースを使用したASP .NETコアアプリの統合テスト

21
Ilya Chumakov

@ ilya-chumakovの答えは素晴らしいです。オプションをもう1つ追加したい

。WebHostBuilderExtensionsのConfigureTestServicesメソッドを使用します。

メソッドConfigureTestServicesは、Microsoft.AspNetCore.TestHostバージョン2.1(20.05.2018ではRC1-final)で使用できます。そして、モックで既存の登録を上書きすることができます。

コード:

_server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureTestServices(services =>
    {
        services.AddTransient<IFooService, MockService>();
    })
);
23
lilo.jacob