2016-12-27 76 views
2

我已經建立了一個WebAPI,除了我在郵差上運行的測試,我想實施一些集成/單元測試。XUnit通過覆蓋啓動文件(.net核心)

現在我的業務邏輯非常薄弱,大部分時間它的更多的CRUD操作,因此我想從測試我的控制器開始。

我有一個基本的設置。存儲庫模式(接口),服務(業務邏輯)和控制器。 流程進入控制器(DI服務) - >服務(DI回購) - >回購操作!

所以我所做的是覆蓋我的啓動文件,以改變到內存數據庫中,其餘的應該沒問題(我會假設)服務被添加,回購被添加,現在我指向一個內存數據庫這是爲我的基本測試罰款。

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是不是有:

下面的構造函數的參數沒有匹配的燈具數據: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)); 
     } 
    } 
} 

什麼時我錯過了?

+0

IIRC,你不能使用在測試類依賴注入。你只能讓xunit通過構造函數注入特殊的燈具(https://xunit.github.io/docs/shared-context.html參見class和collection fixture)。對於集成測試,您需要獲取「IServiceProvider」的實例並讓它解決您的服務。對於控制器測試,您必須使用'TestServer' class => docs.microsoft.com/en-us/aspnet/core/testing/integration-testing – Tseng

+0

此外,您可能不想從Startup.cs繼承,而是有一個單獨的類。用引導代碼。重寫對某些配置不適用(即,當您需要在A之後但在B之前執行代碼時)。註冊兩次相同的接口可能會導致在您調用'GetRequiredService'時發生異常,因爲註冊了多個接口(除非您因此使用'services.TryAddXxx ()' – Tseng

+0

'如果測試類需要訪問夾具實例,添加它作爲構造參數,它會自動提供''所以燈具可以使用DI,但你不能從啓動DI?好吧,這是一個恥辱。當然註冊兩次等等是可以預料的。如果我能通過測試啓動DI,那麼我會在幾秒鐘內完成測試並運行。 – Drakoumel

回答

3

您不能在測試類上使用依賴注入。您只能讓xunit通過構造函數注入特殊裝置(請參閱docs)。

對於集成測試,您希望使用Microsoft.AspNetCore.TestHost包中的TestServer類和單獨的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); 
} 
+0

您剛纔也回答了我對我的問題的評論:)我將盡力在今晚。 非常感謝您的幫助! – Drakoumel

+0

如何讓'TestServer'使用我的'ConfigureTestServices'而不是基類''ConfigureServices'? –

+0

@DmytroBogatov:在'WebHostBuilder'上使用'.UseEnvironment(「Test」)' – Tseng