2017-03-01 104 views
13

我想測試一些屬於ASP.NET Web Api項目的類。我不需要通過TestServer進行請求 - 響應集成測試(雖然它們很好),但我想盡可能保持我的測試接近「真實」。所以我想用Startup中添加的服務來解決我的類,但是在測試的基礎上通過存根/模擬來改變其中的一些(有些測試需要mock,其他測試則不需要)。如何在ASP.NET Core測試中存根/模擬服務

當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>(); 

這似乎工作,但我不想創建每個測試的整個啓動基礎設施建設。我想創建一次,然後爲每個測試創建「子容器」或「子範圍」。可能嗎?基本上我正在尋找一種方法來修改啓動以外的服務。

回答

2

這一切都涵蓋在documentation

對於集成測試,可以使用TestServer類,並給它一個Startup類(不必須是活的啓動,也可以是StartupIntegrationTest或啓動與Configure{Envrionment Name here}/ConfigureServices{Envrionment Name here}方法。

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來解析類,並且我想在解析類之前爲某些接口指定mocks。 – SiberianGuy

+0

閱讀完整答案。最後一段僅用於單元測試,大部分答案都是關於集成測試以及它們應該如何完成的。這就是TestServer的用途,它是Startup的用法。您不必使用該請求,但正如第二個代碼示例中所述,您可以從測試服務器實例訪問IServiceProvider – Tseng

+0

因此,您認爲我們應該爲每個測試創建一個新的TestServer?我只是看不到我如何創建一個TestServer併爲每個測試改變依賴關係(對於我需要的某些測試,對於其他我不需要的測試)。 – SiberianGuy

3

通過創建自定義IHttpControllerActivator來配置您的HttpConfiguration時,可以爲每個請求創建子範圍。

這是OWIN,但轉換到.NET的核心應該是非常簡單的: https://gist.github.com/jt000/eef096a2341471856e8a86d06aaec887

的重要組成部分是在該範圍內建立一個範圍&控制器...

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

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

。 ..和覆蓋默認IHttpControllerActivator ...

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

現在,你可以添加你對照要通過IServiceProvider的創建與範圍的依賴注入ollers ...

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

要在單元測試中測試你的ValuesController,我會建議使用像起訂量框架的東西模擬出你的服務接口的方法。例如:

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

萬一你想用同一Web API Core控制器和DI基礎設施爲您xUnit單元測試(我會要求他們在這種情況下,集成測試),我會提出動議TestServerHttpClient上下文到基類執行xUnitIClassFixture

在這種情況下,你將測試API的一切服務,並DI在你配置你的真實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); 
    } 
} 
相關問題