停止模拟 IFormFile 创建 FileCopy
Stop mocked IFormFile from Creating a FileCopy
在 ASP.Net 核心 Web API 中为我的一项服务 类 编写单元测试时,我需要模拟 IFormFile
。因此我决定像这样模拟它(使用最小起订量):
fileMock.Setup(x => x.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback(() =>
{
Console.WriteLine("File Copied");
})
.Returns(Task.CompletedTask);
我想测试的方法愉快地接受了这个模拟,一切看起来都很好,直到我在这里检查了我为测试目的指定的文件位置:
.
这对我来说似乎有点奇怪,因为我预计不会创建任何文件(特别是因为我的回调和 Return 语句从不接触流)。我试图修改模拟(例如,没有任何回调或立即关闭流)但不幸的是文件仍然被创建。
然后我检查了我的文件保存操作的实现:
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?
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
的创建。
在 ASP.Net 核心 Web API 中为我的一项服务 类 编写单元测试时,我需要模拟 IFormFile
。因此我决定像这样模拟它(使用最小起订量):
fileMock.Setup(x => x.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>()))
.Callback(() =>
{
Console.WriteLine("File Copied");
})
.Returns(Task.CompletedTask);
我想测试的方法愉快地接受了这个模拟,一切看起来都很好,直到我在这里检查了我为测试目的指定的文件位置:
这对我来说似乎有点奇怪,因为我预计不会创建任何文件(特别是因为我的回调和 Return 语句从不接触流)。我试图修改模拟(例如,没有任何回调或立即关闭流)但不幸的是文件仍然被创建。 然后我检查了我的文件保存操作的实现:
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?
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
的创建。