集成測試:固定裝置多於自動裝置

目錄

介紹

背景

使用代碼

興趣點


介紹

已顯示在AutoFixture使用的固定裝置(fixture)對於測試自動化是一個非常有用的概念。固定裝置(fixture)不僅對單元測試有用,而且該概念也可以在集成測試中重用。在本文中,將解釋什麼是固定裝置(fixture),以及該概念如何用於單元測試和集成測試。固定裝置(fixture)總是有一個共同點:它們爲您管理測試的安排部分,這不僅對單元測試,而且對所有類型的測試都是有用的。

背景

您對.NET Core應用程序的TDD有一定的經驗,這將非常有幫助,其中包括模擬,最好是此處使用的.NET Core 3.1。擁有固定裝置(fixture)的經驗也很有用,但不是必需的。本文中顯示的示例基於xUnit,但是概念和技術也可以應用於其他單元測試框架。

使用代碼

首先,我們看一下要測試的代碼。這裏是:

[Route("api/[controller]")]
[ApiController]
public class SearchEngineController : ControllerBase
{
   private readonly ISearchEngineService _searchEngineService;

   public SearchEngineController(ISearchEngineService searchEngineService)
   {
       _searchEngineService = searchEngineService;
   }

   [HttpGet("{queryEntry}", Name = "GetNumberOfCharacters")]
   public async Task<ActionResult<int>> GetNumberOfCharacters(string queryEntry)
   {
       var numberOfCharacters = 
           await _searchEngineService.GetNumberOfCharactersFromSearchQuery(queryEntry);
       return Ok(numberOfCharacters);
   }
}

從上面的代碼中可以清楚地看出,它只是一個控制器方法,它調用某些接口方法並返回帶有OK狀態碼(200)的結果。這是該接口的實現。

public class SearchEngineService : ISearchEngineService
{
   private readonly HttpClient _httpClient;

   public SearchEngineService(HttpClient httpClient)
   {
       _httpClient = httpClient;
   }

   public async Task<int> GetNumberOfCharactersFromSearchQuery(string toSearchFor)
   {
       var result = await _httpClient.GetAsync($"/search?q={toSearchFor}");
       var content = await result.Content.ReadAsStringAsync();
       return content.Length;
   }
}

此代碼也非常簡單。Web請求完成,並返回結果的長度。

這是在Startup類中添加依賴項的方式:

public void ConfigureServices(IServiceCollection services)
{
   services.AddControllers();
   var googleLocation = Configuration["Google"];
   services.AddHttpClient<ISearchEngineService, SearchEngineService>(c =>
            c.BaseAddress = new Uri(googleLocation))
            .SetHandlerLifetime(TimeSpan.FromMinutes(5))
            .AddPolicyHandler(GetRetryPolicy());
}

現在清楚的是,我們需要能夠模擬兩個依賴關係:

  • 內部依賴項:這是ISearchEngineService的實例注入控制器。
  • 外部依賴項:這是設置爲添加HttpClient內容基址的搜索引擎(url)。對於集成測試,我們需要模擬一下,因爲我們對其是否給出相同的響應並保持在線狀態沒有任何影響。

內部依賴項的模擬通常是出於單元測試的目的,使用Moq可以如本文所述。外部依賴項的模擬通常是出於集成測試目的而進行的,如本文所述,使用WireMock ,此處提供的一些代碼來自此。此處顯示的所有代碼在GitHub也可用。

這是使用固定裝置(fixture)進行單元測試的樣子:

[Fact]
public async Task GetTest()
{
   // arrange
   var fixture = new Fixture().Customize(new AutoMoqCustomization());
   var service = fixture.Freeze<Mock<ISearchEngineService>>();
   service.Setup(a => a.GetNumberOfCharactersFromSearchQuery(It.IsNotNull<string>()))
                .ReturnsAsync(8);
   var controller = fixture.Build<SearchEngineController>().OmitAutoProperties().Create();

   // act
   var response = await controller.GetNumberOfCharacters("Hoi");

   // assert
   Assert.Equal(8, ((OkObjectResult)response.Result).Value);
   service.Verify(s => s.GetNumberOfCharactersFromSearchQuery("Hoi"), Times.Once);
}

我們在這裏看到的是以下內容:

  1. 第一步是創建固定裝置。
  2. 之後, 調用Freeze方法(在Create方法之前)以指定內部依賴性,我們希望在測試結束時進行一些驗證。
  3. 內部依賴關係需要與Setup方法一起設置。
  4. 在測試方法的assert部分中,我們驗證對內部依賴項(service實例)的調用

如測試的實現,需要一個NuGet包:AutoFixture.AutoMoq

.NET Core的最新版本開始,集成測試變得更加容易。與單元測試的主要區別在於集成更加真實(因此配置性更差)。在Startup類和Program類都包括在內。使用實際的內部依賴關係來代替模擬/存根(stubs)。只有外部依賴項需要模擬,因爲它們可以脫機,具有變化的行爲或不穩定,所有這些都不應破壞測試。這是使用固定裝置(fixture)進行集成測試的方法:

[Fact]
public async Task GetTest()
{
   // arrange
   using (var fixture = new Fixture<Startup>())
   {
      using (var mockServer = fixture.FreezeServer("Google"))
      {
          SetupStableServer(mockServer, "Response");
          var controller = fixture.Create<SearchEngineController>();

          // act
          var response = await controller.GetNumberOfCharacters("Hoi");

          // assert
          var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
          Assert.Contains("Hoi", request.RawQuery);
          Assert.Equal(8, ((OkObjectResult)response.Result).Value);
       }
    }
}

private void SetupStableServer(FluentMockServer fluentMockServer, string response)
{
    fluentMockServer.Given(Request.Create().UsingGet())
    .RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8)
    .WithStatusCode(HttpStatusCode.OK));
}

我們在這裏看到的是類似的:

  1. 第一步是使用Startup類創建固定裝置(fixture),一旦Create方法被調用,它就初始化所有依賴項。
  2. 之後,調用FreezeServer方法(在該Create方法之前)以設置外部依賴關係,我們希望在測試結束時進行一些驗證。
  3. 內部依賴性需要使用一種自寫SetupStableServer方法來設置,該方法描述某個請求需要某個響應。這將在此處更詳細地說明。
  4. 在測試方法的assert部分中,我們驗證發送到外部依賴項(模擬服務)的Web請求

實現諸如測試,需要另一個NuGet包:ConnectingApps.IntegrationFixture

從上面的代碼可以清楚地看到,使用固定裝置(fixture)進行集成測試的方式與使用固定裝置(fixture)進行單元測試的方式基本相同。但是,除了安排和驗證內部依賴項外,還需要使用外部依賴項來完成。他的建議聽起來很明顯,但是在不使用夾具的情況下進行集成測試時,需要大量的樣板以作解釋

興趣點

在研究固定裝置(fixture),使用固定裝置(fixture)並撰寫本文時,對我來說清楚的是,固定裝置(fixture)的概念可以以多種方式使用。AutoFixture在編寫單元測試的安排部分時很有用,但其固定裝置(fixture)也可用於其他類型測試的(安排部分)。它使我們作爲開發人員不必編寫大量樣板代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章