xunit test for IFormFile field in Asp.net Core

风流意气都作罢 提交于 2019-12-22 06:42:13

问题


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.


回答1:


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




回答2:


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

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



回答3:


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);

}


来源:https://stackoverflow.com/questions/44129143/xunit-test-for-iformfile-field-in-asp-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!