Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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.

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

The problem is that the await formFile.CopyToAsync(memoryStream, CancellationToken.None) doesn't copy any data to the memoryStream.

like image 665
Jeroen Avatar asked Nov 15 '17 13:11

Jeroen


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C language?

C is a structured, procedural programming language that has been widely used both for operating systems and applications and that has had a wide following in the academic community. Many versions of UNIX-based operating systems are written in C.


Video Answer


2 Answers

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. 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());
}
like image 194
nvoigt Avatar answered Oct 06 '22 10:10

nvoigt


The problem is that the await formFile.CopyToAsync(memoryStream, CancellationToken.None) doesn't copy any data to the 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.

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);
like image 8
Nkosi Avatar answered Oct 06 '22 10:10

Nkosi