xunit test for IFormFile field in Asp.net Core

Viewed 10014

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.

3 Answers

you can create an actual instance just like that...

bytes[] filebytes = Encoding.UTF8.GetBytes("dummy image");
IFormFile file = new FormFile(new MemoryStream(filebytes), 0, filebytes.Length, "Data", "image.png");

I had to unit test proper jpeg file upload with correct User, so:

private ControllerContext RequestWithFile()
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers.Add("Content-Type", "multipart/form-data");

var sampleImagePath = host.WebRootPath + SampleImagePath; //path to correct image    
var b1 = new Bitmap(sampleImagePath).ToByteArray(ImageFormat.Jpeg);

MemoryStream ms = new MemoryStream(b1);    
var fileMock = new Mock<IFormFile>();

fileMock.Setup(f => f.Name).Returns("files");
fileMock.Setup(f => f.FileName).Returns("sampleImage.jpg");
fileMock.Setup(f => f.Length).Returns(b1.Length);
fileMock.Setup(_ => _.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Returns((Stream stream, CancellationToken token) => ms.CopyToAsync(stream))
.Verifiable();              

string val = "form-data; name=";      

val += "\\";
val += "\"";
val += "files";
val += "\\";
val += "\"";
val += "; filename=";
val += "\\";
val += "\"";
val += "sampleImage.jpg";
val += "\\";
val += "\"";


fileMock.Setup(f => f.ContentType).Returns(val);
fileMock.Setup(f => f.ContentDisposition).Returns("image/jpeg");      


httpContext.User = ClaimsPrincipal; //user part, you might not need it
httpContext.Request.Form = 
new FormCollection(new Dictionary<string, StringValues>(), new FormFileCollection { fileMock.Object });
var actx = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());

return new ControllerContext(actx);
}

Then in test:

[Fact]
public async void TestUploadFile_byFile()
{
var amountOfPosts = _dbContext.Posts.Count();
var amountOfPics = _dbContext.SmallImages.Count();

sut.ControllerContext = RequestWithFile();
var ret = await sut.UploadNewDogePOST(new Doge.Areas.User.Models.UploadDoge());

var amountOfPosts2 = _dbContext.Posts.Count();
var amountOfPics2 = _dbContext.SmallImages.Count();

Assert.True(amountOfPosts < amountOfPosts2);
Assert.True(amountOfPics < amountOfPics2);

var lastImage = _dbContext.SmallImages.Include(im => im.DogeBigImage).Last();
var sampleImagePath = host.WebRootPath + SampleImagePath;
var b1 = new Bitmap(sampleImagePath).ToByteArray(ImageFormat.Jpeg);

Assert.True(b1.Length == lastImage.DogeBigImage.Image.Length);

Assert.IsType<RedirectToActionResult>(ret);
Assert.Equal("Index", ((RedirectToActionResult)ret).ActionName);

}
Related