I have an Asp.net Core method with below definition.
[HttpPost]
public IActionResult Upload(IFormFile file)
{
if (file == null || file.Length == 0)
throw new Exception("file should not be null");
var originalFileName = ContentDispositionHeaderValue
.Parse(file.ContentDisposition)
.FileName
.Trim('"');
file.SaveAs("your_file_full_address");
}
I want to create XUnit Test for this function, how could I mock IFormFile
?
Update:
Controller:
[HttpPost]
public async Task<ActionResult> Post(IFormFile file)
{
var path = Path.Combine(@"E:\path", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok();
}
Xunit Test
[Fact]
public async void Test1()
{
var file = new Mock<IFormFile>();
var sourceImg = File.OpenRead(@"source image path");
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(sourceImg);
writer.Flush();
stream.Position = 0;
var fileName = "QQ.png";
file.Setup(f => f.OpenReadStream()).Returns(stream);
file.Setup(f => f.FileName).Returns(fileName);
file.Setup(f => f.Length).Returns(stream.Length);
var controller = new ValuesController();
var inputFile = file.Object;
var result = await controller.Post(inputFile);
//Assert.IsAssignableFrom(result, typeof(IActionResult));
}
But, I got empty image in the target path.
To write a test you simply create a public method that returns nothing and then decorate it with the Fact attribute. Inside that method you can put whatever code you want but, typically, you'll create some object, do something with it and then check to see if you got the right result using a method on the Assert class.
What is IFormFile. ASP.NET Core has introduced an IFormFile interface that represents transmitted files in an HTTP request. The interface gives us access to metadata like ContentDisposition, ContentType, Length, FileName, and more. IFormFile also provides some methods used to store files.
The MSBuild runner in xUnit.net v2 is capable of running unit tests from both xUnit.net v1 and v2. It can run multiple assemblies at the same time, and build file options can be used to configuration the parallelism options used when running the tests.
When testing with IFormFile
dependencies, mock the minimal necessary members to exercise the test. In the Controller above FileName
property and CopyToAsync
method are used. Those should be setup for the test.
public async Task Test1() {
// Arrange.
var file = new Mock<IFormFile>();
var sourceImg = File.OpenRead(@"source image path");
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
writer.Write(sourceImg);
writer.Flush();
ms.Position = 0;
var fileName = "QQ.png";
file.Setup(f => f.FileName).Returns(fileName).Verifiable();
file.Setup(_ => _.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns((Stream stream, CancellationToken token) => ms.CopyToAsync(stream))
.Verifiable();
var controller = new ValuesController();
var inputFile = file.Object;
// Act.
var result = await controller.Post(inputFile);
//Assert.
file.Verify();
//...
}
Though mentioned in the comments that the question is just a demo, the tight coupling to the file system should be abstracted to allow for better flexibility
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With