简体   繁体   English

c#mocking IFormFile CopyToAsync()方法

[英]c# mocking IFormFile CopyToAsync() method

I am working on a unit test for an async function that converts a list of IFormFile to a list of my own arbitrary database file classes. 我正在进行异步函数的单元测试,该函数将IFormFile列表转换为我自己的任意数据库文件类列表。

The method that converts the file data to a byte array is: 将文件数据转换为字节数组的方法是:

internal async Task<List<File>> ConvertFormFilesToFiles(ICollection<IFormFile> formFiles)
{
    var file = new File
    {
        InsertDateTime = DateTime.Now,
        LastChangeDateTime = DateTime.Now
    };
    if (formFile.Length > 0)
    {
        using (var memoryStream = new MemoryStream())
        {
            await formFile.CopyToAsync(memoryStream, CancellationToken.None);
            file.FileData = memoryStream.ToArray();
        }
    }
    // ...
}

The function receives an ICollection of IFormFiles so it is mockable. 该函数接收IFormFiles的ICollection,因此它是可模拟的。

For now I have test code like this: 现在我有这样的测试代码:

//Arrange
var fileConverter = new FilesConverter();
using (var fileStream = new FileStream("Files/uploadme.txt", FileMode.Open, FileAccess.Read))
{
    using (var memoryStream = new MemoryStream())
    {
        var newMemoryStream = new MemoryStream();
        fileStream.CopyTo(memoryStream);
        FormFileMock.Setup(f => f.CopyToAsync(newMemoryStream, CancellationToken.None)).Returns(Task.CompletedTask);
        // some other setups

        //Act
        var result = await fileConverter.ConvertFormFilesToFiles(new List<IFormFile> { FormFileMock.Object });
        //Assert
        Assert.IsTrue(result.Any());
    }
}

The newMemoryStream variable is created because the function calls the CopyToAsync method with a new and empty memorystream (I'm not sure if this is necessary). 创建newMemoryStream变量是因为该函数使用新的空内存流调用CopyToAsync方法(我不确定是否有必要)。

The problem is that the await formFile.CopyToAsync(memoryStream, CancellationToken.None) doesn't copy any data to the memoryStream. 问题是await formFile.CopyToAsync(memoryStream,CancellationToken.None)不会将任何数据复制到memoryStream。

I know this might be unpopular because using a mock framework is totally "in" these days, but why not simply leave the framework be framework and go the simple, easy way? 我知道这可能不受欢迎,因为现在使用模拟框架完全“在”,但为什么不简单地让框架成为框架并采用简单,简单的方法? You can create a FormFile without any mocking. 您可以在不进行任何FormFile情况下创建FormFile Just the real deal: 真正的交易:

var fileConverter = new FilesConverter(FilesConverterLoggerMock.Object, FileDataMock.Object);

// access to a real file should really not be in a "unit" test, but anyway: 
using (var stream = new MemoryStream(File.ReadAllBytes("Files/uploadme.txt"))
{
  // create a REAL form file
  var formFile = new FormFile(stream , 0, stream.Length, "name", "filename");

  //Act
  var result = await fileConverter.ConvertFormFilesToFiles(new List<IFormFile> { formFile });

  //Assert
  Assert.IsTrue(result.Any());
}

The problem is that the await formFile.CopyToAsync(memoryStream, CancellationToken.None) doesn't copy any data to the memoryStream . 问题是await formFile.CopyToAsync(memoryStream, CancellationToken.None)不会将任何数据复制到memoryStream

According to your setup. 根据你的设置。 Nothing will actually be copied. 什么都不会被复制。

You just setup the call to return as completed. 您只需将呼叫设置为已完成返回。 No actual functionality was implemented. 没有实现任何实际功能。

You'll need to add a Callback to perform some desired functionality before returning the task. 在返回任务之前,您需要添加一个Callback来执行一些所需的功能。

FormFileMock
    .Setup(_ => _.CopyToAsync(It.IsAny<Stream>(), CancellationToken.None))
    .Callback<Stream, CancellationToken>((stream, token) => {
        //memory stream in this scope is the one that was populated
        //when you called **fileStream.CopyTo(memoryStream);** in the test
        memoryStream.CopyTo(stream);
    }) 
    .Returns(Task.CompletedTask);

I combined the answers of @Nkosi and @nvoigt. 我结合了@Nkosi和@nvoigt的答案。 As @nvoigt pointed out: access to a real file should really not be in a "unit" test. 正如@nvoigt指出的那样:访问真实文件应该不是在“单元”测试中。 So I replaced the file with a byte array like so: 所以我用一个像这样的字节数组替换了文件:

using (var memoryStream = new MemoryStream(new byte[]{1,2,3,4}))

Instead of a complete file. 而不是一个完整的文件。

And I implemented the .callback on the mocked object as suggested by @Nkosi 我按照@Nkosi的建议在模拟对象上实现了.callback

FormFileMock.Setup(f => f.CopyToAsync(It.IsAny<Stream>(), CancellationToken.None))
    .Callback<Stream, CancellationToken>((stream, token) =>
    {
        // with memoryStream being the stream from inside the using statement
        memoryStream.CopyTo(stream);
    }).Returns(Task.CompletedTask);

And now it works. 现在它有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM