停止模拟的IFormFile创建FileCopy

时间:2018-10-30 00:23:40

标签: c# unit-testing moq asp.net-core-webapi

在ASP.Net Core Web API中为我的一个服务类编写单元测试时,我需要模拟IFormFile。因此,我决定使用moq这样模拟它:

fileMock.Setup(x => x.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback(() =>
{
    Console.WriteLine("File Copied");
})
.Returns(Task.CompletedTask);

我要测试的方法愉快地接受了该模拟,一切似乎都很好,直到我检查了在此处为测试目的指定的文件位置:
here

这对我来说有点奇怪,因为我希望不会创建任何文件(特别是因为我的Callback和Return语句从不接触流)。我尝试修改了模拟程序(例如,没有任何回调或立即关闭Stream),但仍然无法创建文件。 然后,我检查了文件保存操作的实现:

public async Task<Result> SaveFileToDiskAsync(string filePath, IFormFile file, CancellationToken token)
{
    //Checking if values are correct
    try
    {
        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await file.CopyToAsync(stream, token).ConfigureAwait(false);
            return Result.Ok();
        }               
    }
    catch (Exception e)
    {
     //Logging
    }
}

这就是我被困住的地方。我没有看到此方法有什么问题,特别是因为它似乎在复制文件时做正确的事情(它只是在错误的时间执行)。 因此,我的问题是:有没有更好的方法来实现模拟或方法来停止FileCreation?

1 个答案:

答案 0 :(得分:3)

var stream = new FileStream(filePath, FileMode.Create) 

创建文件。

系统与实际的IO实现关注紧密联系在一起。不是可以嘲笑的抽象。

抽象文件流访问。

public interface IFileStreamProvider {
    Stream Create(string path);
    Stream Open(string path);
    //...
}

这应该使系统具有与实现关注点脱钩的灵活性。

private readonly IFileStreamProvider disk; //populated via constructor injection.

public async Task<Result> SaveFileToDiskAsync(string filePath, IFormFile file, CancellationToken token) {
    //Checking if values are correct
    try {
        using (var stream = disk.Create(filePath)) {
            await file.CopyToAsync(stream, token).ConfigureAwait(false);
            return Result.Ok();
        }               
    } catch (Exception e) {
        //Logging
    }
}

并进行了隔离测试

//...

var disk = new MemoryStream(); //
var diskMock = new Mock<IFileStreamProvider>();
diskMock
    .Setup(_ => _.Create(It.IsAny<string>()))
    .Returns(disk);

//...

虽然实际的IFileStreamProvider.Create实现将包装FileStream的创建。