I have trouble trying to create an integration test for my post call that accepts a viewmodel that has amongst other values, an IFormFile, which makes this call from an application/json to a multipart/form-data
My IntegrationSetup class
protected static IFormFile GetFormFile()
{
byte[] bytes = Encoding.UTF8.GetBytes("test;test;");
var file = new FormFile(
baseStream: new MemoryStream(bytes),
baseStreamOffset: 0,
length: bytes.Length,
name: "Data",
fileName: "dummy.csv"
)
{
Headers = new HeaderDictionary(),
ContentType = "text/csv"
};
return file;
}
My Test Method
public async Task CreateAsync_ShouldReturnId()
{
//Arrange
using var content = new MultipartFormDataContent();
var stringContent = new StringContent(
JsonConvert.SerializeObject(new CreateArticleViewmodel
{
Title = "viewModel.Title",
SmallParagraph = "viewModel.SmallParagraph",
Url = "viewModel.Url",
Image = GetFormFile()
}),
Encoding.UTF8,
"application/json");
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
//Act
var response = await httpClient.PostAsync($"{Url}", content);
//Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
int id = int.Parse(await response.Content.ReadAsStringAsync());
id.ShouldBeGreaterThan(0);
}
My Controller Method
[HttpPost]
public async Task<IActionResult> CreateArticleAsync([FromForm] CreateArticleViewmodel viewModel)
{
var id = await _service.CreateAsync(viewModel).ConfigureAwait(false);
if (id > 0)
return Ok(id);
return BadRequest();
}
It throws a BadRequest without getting inside the method.
The way you are posting the request contents to the API, in your code, is not correct.
When the API expects a FileInfo
in the request payload, posting JSON content never works. You need to send the payload as MultipartFormData
and not as JSON.
Consider following example.
This is a an API endpoint which expects and model with FileInfo in it as payload.
[HttpPost]
public IActionResult Upload([FromForm] MyData myData)
{
if (myData.File != null)
{
return Ok("File received");
}
else
{
return BadRequest("File no provided");
}
}
public class MyData
{
public int Id { get; set; }
public string Title { get; set; }
// Below property is used for getting file from client to the server.
public IFormFile File { get; set; }
}
This is pretty much the same API as yours.
Following is the client code which calls the above API with file and other model properties.
var apiURL = "http://localhost:50492/home/upload";
const string filename = "D:\\samplefile.docx";
HttpClient _client = new HttpClient();
// Instead of JSON body, multipart form data will be sent as request body.
var httpContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(File.ReadAllBytes(filename));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// Add File property with file content
httpContent.Add(fileContent, "file", filename);
// Add id property with its value
httpContent.Add(new StringContent("789"), "id");
// Add title property with its value.
httpContent.Add(new StringContent("Some title value"), "title");
// send POST request.
var response = await _client.PostAsync(apiURL, httpContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
// output the response content to the console.
Console.WriteLine(responseContent);
The client code is running from a Console application. So when I run this, the expectation is to get File received
message in the console and I am getting that message.
Following is the screen capture of the model content at the API end while debugging it.
And if I am calling this API from postman, it would look like following.
I hope this will help you solve your issue.