2017-03-01 9 views
13

ASP.NET Web APIプロジェクトに含まれるいくつかのクラスをテストしたいと思います。私はTestServer経由でリクエスト/レスポンスの統合テストをする必要はありませんが、テストはできるだけ「本物」に近いものにしたいと思っています。そこでスタートアップで追加されたサービスを使用してクラスを解決したいが、テストベースでスタブ/モックのいくつかを変更する(いくつかのテストにはモックが必要なものもあれば、そうでないものもある)。ASP.NETコアテストでサービスをスタブ/モックする方法

ASP.NETが内部依存性注入フレームワークを持っていない時代には、本当に簡単でした。だから、私はちょうどコンテナにすべての依存関係を登録し、すべてのテストのために子コンテナを作成し、いくつかの依存関係をモックに変更するクラスを呼び出すだけです。

var host = A.Fake<IHostingEnvironment>(); 
    var startup = new Startup(host); 
    var services = new ServiceCollection(); 
    //Add stubs here 
    startup.ConfigureServices(services); 
    var provider = services.BuildServiceProvider(); 
    provider.GetService<IClientsHandler>(); 

動作しているようですが、私はすべてのテストのために全体の起動インフラストラクチャを作成したくない:

私はこのような何かを試してみました。私はそれを一度作成し、各テストの "子コンテナ"または "子スコープ"を作成したいと思います。出来ますか?基本的には、Startup以外のサービスを変更する方法を探しています。

答えて

2

documentationで詳しく説明しています。統合テストのために

、あなたは(ライブ起動している必要はありません、またConfigure{Envrionment Name here}/ConfigureServices{Envrionment Name here}方法StartupIntegrationTestまたは起動することができTestServerクラスを使用し、それをStartupクラスを与える。

var server = new TestServer(new WebHostBuilder() 
    .UseStartup<Startup>() 
    // this would cause it to use StartupIntegrationTest class or ConfigureServicesIntegrationTest/ConfigureIntegrationTest methods (if existing) 
    // rather than Startup, ConfigureServices and Configure 
    .UseEnvironment("IntegrationTest")); 

アクセスするにはサービスプロバイダは、ユニットテストのために

var server = new TestServer(new WebHostBuilder() 
    .UseStartup<Startup>() 
    .UseEnvironment("IntegrationTest")); 
var controller = server.Host.Services.GetService<MyService>(); 

を行うあなただけのインターフェイスを模擬し、それらを注入し、全くIServiceCollection/IServiceProviderを使用しないでください。

+1

しかし、私は模擬インターフェイスを手動で注入したくありません。私はServicesを使ってクラスを解決したいので、クラスを解決する前にいくつかのインターフェイスのモックを指定したいと思います。 – SiberianGuy

+0

答え全体を読む。最後の段落は単体テストのためのものであり、答えの大部分は統合テストを行う方法とそれらがどのように行われることになっているかです。それはTestServerのためのもので、Startupの使用法が作られています。しかし、2番目のコード例に記載されているように、テストサーバーインスタンス – Tseng

+0

からIServiceProviderにアクセスするだけでテストごとに新しいTestServerを作成する必要がありますか? TestServerを1つ作成して各テストの依存関係を変更する方法がわかりません(いくつかのテストではモックが必要ですが、他のテストではそうしません)。 – SiberianGuy

3

すべての要求に対して子スコープを作成するには、カスタムIHttpControllerActivatorを作成してHttpConfigurationを構成する必要があります。

これはOWINのためですが、ネットコアに変換することが非常に簡単でなければなりません: https://gist.github.com/jt000/eef096a2341471856e8a86d06aaec887

重要な部分は、そのスコープ内のスコープ&コントローラを作成するためのものです...

var scope = _provider.CreateScope(); 
request.RegisterForDispose(scope); 

var controller = scope.ServiceProvider.GetService(controllerType) as IHttpController; 

。 ..andデフォルトIHttpControllerActivatorを上書き...

config.Services.Replace(typeof (IHttpControllerActivator), new ServiceProviderControllerActivator(parentActivator, provider)); 

今すぐあなたのCONTRを追加することができますあなたのユニットテストの中にあなたのValuesControllerをテストするにはスコープの依存性注入とIServiceProviderを介して作成するollers ...

services.AddScoped<ValuesController>((sp) => new ValuesController(sp.GetService<ISomeCustomService>())); 

は、私はあなたのサービス・インターフェースのメソッドをモックする部品番号のフレームワークのようなものを使用してお勧めします。たとえば:

var someCustomService = Mock.Of<ISomeCustomService>(s => s.DoSomething() == 3); 
var sut = new ValuesController(someCustomService); 

var result = sut.Get(); 

Assert.AreEqual(result, new [] { 3 }); 
+0

私はそれをチェックする時間がありませんでしたが、それは有望に見えます。ありがとう! – SiberianGuy

1

は、念のためにあなたは(私は彼らこの場合、統合テストを呼ぶだろう)あなたのxUnitユニットテストのために同じWeb API CoreコントローラとDIインフラストラクチャを使用したいと思い、私が移動するために提案するTestServerHttpClientコンテキストを基底クラスに実装すると、xUnitIClassFixtureが実装されます。この場合

あなたに設定されているすべてのサービスをAPIDIをテストしますあなたの本当のWeb API Core

その後
public class TestServerDependent : IClassFixture<TestServerFixture> 
{ 
    private readonly TestServerFixture _fixture; 
    public TestServer TestServer => _fixture.Server; 
    public HttpClient Client => _fixture.Client; 

    public TestServerDependent(TestServerFixture fixture) 
    { 
     _fixture = fixture; 
    } 

    protected TService GetService<TService>() 
     where TService : class 
    { 
     return _fixture.GetService<TService>(); 
    } 
} 

public class TestServerFixture : IDisposable 
{ 
    public TestServer Server { get; } 
    public HttpClient Client { get; } 

    public TestServerFixture() 
    { 
     // UseStaticRegistration is needed to workaround AutoMapper double initialization. Remove if you don't use AutoMapper. 
     ServiceCollectionExtensions.UseStaticRegistration = false; 

     var hostBuilder = new WebHostBuilder() 
      .UseEnvironment("Testing") 
      .UseStartup<Startup>(); 

     Server = new TestServer(hostBuilder); 
     Client = Server.CreateClient(); 
    } 

    public void Dispose() 
    { 
     Server.Dispose(); 
     Client.Dispose(); 
    } 

    public TService GetService<TService>() 
     where TService : class 
    { 
     return Server?.Host?.Services?.GetService(typeof(TService)) as TService; 
    } 
} 

この方法コントローラのアクションをテストするには、このクラスから派生ちょうどすることができます

public class ValueControllerTests : TestServerDependent 
{ 
    public ValueControllerTests(TestServerFixture fixture) 
     : base(fixture) 
    { 
    } 

    [Fact] 
    public void Returns_Ok_Response_When_Requested() 
    { 
     var responseMessage = Client.GetAsync("/api/value").Result; 
     Assert.Equal(HttpStatusCode.OK, responseMessage.StatusCode); 
    } 
} 

また、DIサービスをテストすることもできます。

public class MyServiceTests : TestServerDependent 
{ 
    public MyServiceTests(TestServerFixture fixture) 
     : base(fixture) 
    { 
    } 

    [Fact] 
    public void ReturnsDataWhenServiceInjected() 
    { 
     var service = GetService<IMyService>(); 
     Assert.NotNull(service); 

     var data = service.GetData(); 
     Assert.NotNull(data); 
    } 
} 
関連する問題