簡體   English   中英

將 FromForm 綁定到 IFormFile 屬性的 C# 集成測試控制器

[英]C# Integration Testing Controller with FromForm binding to IFormFile property

我正在使用:

  • Visual Studio 2017 專業版
  • dotnet 核心 SDK 2.2.102
  • XUnit 2.4.1

我想做什么

集成測試接受表單數據的 API 控制器方法。

設置

  • 我的控制器中的 API 路由使用[FromForm]屬性接受 CommandObject
  • CommandObject 的屬性之一是List<IFormFile>類型,它旨在管理屬於請求的任何文件
  • 當我從 Postman 手動測試時,Controller 方法按預期工作。

問題

文件未綁定到List<IFormFile>屬性。 其他一切都按預期工作,但文件沒有。 這是我第一次使用 Multipart Form Data,所以不知道該嘗試什么。

當我調試測試時,您可以看到除Documents屬性之外的所有內容都正常工作(注意,這與下面的代碼不 100% 匹配,因為我不得不混淆了一些東西)

在此處輸入圖片說明

我看過的東西

有很多與多部分表單數據相關的東西,我嘗試過的一些解決方案是:

MyIntegrationTest.cs

我的集成測試設置背后有很多代碼。 如果我把它都貼在這里,我認為它不會很有幫助。 最重要的一條信息是變量server的類型為Microsoft.AspNetCore.TestHost.TestServer

[Fact]
async Task Post_ItemAsync_HappyPath_ReturnsOKStatusCode()
{
    var fileDir = @"C:/path/to/files";
    var fileNames = new string[] { "test.docx", "test.txt" };

    using (var server = CreateTestServer())
    {
        // Arrange
        var formData = new MultipartFormDataContent()
        {
            { new StringContent("Test Title"), "Title" },
            { new StringContent("Test Description"), "Description" },
            { new StringContent("String_1"), "AListOfStrings" },
            { new StringContent("String_2"), "AListOfStrings" },
            { new StringContent("3"), "NumberOfThings" }
        };

        foreach (var fileName in fileNames)
        {
            var document = File.ReadAllBytes($"{fileDir}/{fileName}");
            formData.Add(new ByteArrayContent(document), "file", fileName);
        }

        string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
        string contentType = "multipart/form-data; boundary=" + formDataBoundary;

        var request = new HttpRequestMessage(HttpMethod.Post, "api/v1/item")
        {
            Headers =
            {
                { HttpRequestHeader.ContentType.ToString(), contentType }
            },
            Content = formData
        };

        // Act
        var response = await server.CreateClient().SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        // Cleanup
        ...
    }
}

我的控制器

[HttpPost]
ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> CreateItemAsync([FromForm]CreateItemCommand command)
{
    bool commandResult = false;

    commandResult = await _mediator.Send(command);

    if (!commandResult)
    {
        return BadRequest();
    }

    return Ok();
}

創建項目命令.cs

[DataContract]
public class CreateItemCommand
    :IRequest<bool>
{
    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Description { get; set; }

    [DataMember]
    public HashSet<string> AListOfThings { get; set; }

    [DataMember]
    public int NumberOfThings { get; set; }

    [DataMember]
    public List<IFormFile> Documents { get; private set; }

    public CreateITemCommand()
    {
        AListOfThings = new HashSet<string>();
    }

    public CreateItemCommand(string title, string description, HashSet<string> aListOfThings, int NumberOfThings, List<IFormFile> documents)
        : this()
    {
        Title = title;
        Description = description;
        AListOfStrings = aListOfStrings;
        NumberOfThings = numberOfThings;
        Documents = documents;
    }
}

表單數據邊界應在初始化時添加到MultipartFormDataContent ,並且文件的名稱需要與要填充的模型所需的屬性相匹配。

//...

// Arrange
string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());

var formData = new MultipartFormDataContent(formDataBoundary) { //<---- NOTE HERE
    { new StringContent("Test Title"), "Title" },
    { new StringContent("Test Description"), "Description" },
    { new StringContent("String_1"), "AListOfStrings" },
    { new StringContent("String_2"), "AListOfStrings" },
    { new StringContent("3"), "NumberOfThings" }
};

foreach (var fileName in fileNames) {
    var document = File.ReadAllBytes($"{fileDir}/{fileName}");
    formData.Add(new ByteArrayContent(document), "Documents", fileName); //<-- NOTE HERE
}

// Act
var response = await server.CreateClient().PostAsync("api/v1/item", formData);

//...

模型的Documents屬性需要設置為 public 以便模型綁定器可以在解析表單數據時填充它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM