Asp.net Core 中 IFormFile 字段的 xunit 测试
xunit test for IFormFile field in Asp.net Core
我有一个 Asp.net 核心方法,其定义如下。
[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");
}
我想为此函数创建 XUnit 测试,如何模拟 IFormFile
?
更新:
控制器:
[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 测试
[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));
}
但是,我在目标路径中得到了空图像。
在使用 IFormFile
依赖项进行测试时,模拟最少的必需成员来执行测试。在上面的 Controller 中使用了 FileName
属性 和 CopyToAsync
方法。这些应该为测试设置。
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();
//...
}
虽然在评论中提到问题只是一个演示,但应该抽象出与文件系统的紧密耦合以提供更好的灵活性
您可以像这样创建一个实际实例...
bytes[] filebytes = Encoding.UTF8.GetBytes("dummy image");
IFormFile file = new FormFile(new MemoryStream(filebytes), 0, filebytes.Length, "Data", "image.png");
我必须使用正确的用户对正确的 jpeg 文件上传进行单元测试,所以:
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);
}
然后在测试中:
[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);
}
我有一个 Asp.net 核心方法,其定义如下。
[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");
}
我想为此函数创建 XUnit 测试,如何模拟 IFormFile
?
更新:
控制器:
[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 测试
[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));
}
但是,我在目标路径中得到了空图像。
在使用 IFormFile
依赖项进行测试时,模拟最少的必需成员来执行测试。在上面的 Controller 中使用了 FileName
属性 和 CopyToAsync
方法。这些应该为测试设置。
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();
//...
}
虽然在评论中提到问题只是一个演示,但应该抽象出与文件系统的紧密耦合以提供更好的灵活性
您可以像这样创建一个实际实例...
bytes[] filebytes = Encoding.UTF8.GetBytes("dummy image");
IFormFile file = new FormFile(new MemoryStream(filebytes), 0, filebytes.Length, "Data", "image.png");
我必须使用正确的用户对正确的 jpeg 文件上传进行单元测试,所以:
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);
}
然后在测试中:
[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);
}