Я использую:
- Visual Studio 2017 Pro
- dotnet core sdk 2.2.102
- XUnit 2.4.1
Что я пытаюсь сделать
Интеграционный тест метода контроллера API, который принимает данные формы.
Настройка
- API-маршрут в моем контроллере, который принимает CommandObject с использованием атрибута
[FromForm]
- Одно из свойств CommandObject имеет тип
List<IFormFile>
, оно предназначено для управления любыми файлами, которые являются частью запроса
- Метод 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
...
}
}
MyController.cs
[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();
}
CreateItemCommand.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;
}
}