c# - 使用 Moq .net Core 对文件上传进行单元测试

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

我在 WebApi Controller 中有一个方法,我想为其编写单元测试。这是我的 Controller 方法的样子:

Controller.cs

public async Task<FileUploadDto> UploadGoalDocument(Guid id)
    {
        var file = this.Request?.Form?.Files.FirstOrDefault();
        FileUploadDto result = null;

        if (file == null)
        {
            return this.CreateResponse(result);
        }

        //logic to store file in db

        return this.CreateResponse(new FileUploadDto() { Id = document.Id, Name = document.Name, Uri = document.Uri});
    }

如何在单元测试中模拟请求对象?我尝试了以下操作,但遇到了 IFormFileCollection 的问题。以下行抛出错误:

未找到 system.argumentexception 接口(interface)

cc.Setup(x => x.HttpContext.Request.Form.Files).Returns(col.Object);

ControllerTest.cs

public async Task Upload_document_should_upload_document_and_return_dto()
    {
        var fileDto = new FileUploadDto { Id = Guid.NewGuid(), Name = "dummy.txt" };

        var fileMock = new Mock<IFormFile>();
        //Setup mock file using a memory stream
        using (var ms = new MemoryStream())
        {
            using (var writer = new StreamWriter("dummy.txt"))
            {
                writer.WriteLine("Hello World from a Fake File");
                writer.Flush();
                ms.Position = 0;
                fileMock.Setup(m => m.OpenReadStream()).Returns(ms);

                var file = fileMock.Object;
                this.goalService.Setup(m => m.UploadDocument(Guid.NewGuid(), file, ""))
                    .ReturnsAsync(new Services.DTO.FileUploadDto { Id = fileDto.Id, Name = fileDto.Name });

                var cc = new Mock<ControllerContext>();
                var col = new Mock<IFormFileCollection>();
                col.Setup(x=> x.GetFile("dummy.txt")).Returns(file);
                cc.Setup(x => x.HttpContext.Request.Form.Files).Returns(col.Object);
                this.controller.ControllerContext = cc.Object;
                var result = await this.controller.UploadGoalDocument(Guid.NewGuid());

                //Asserts removed for simplicity
            }
        }
    }

详细堆栈跟踪:

System.RuntimeTypeHandle.VerifyInterfaceIsImplemented(RuntimeTypeHandle handle, RuntimeTypeHandle interfaceHandle)
at System.RuntimeType.GetInterfaceMap(Type ifaceType)
at Moq.Extensions.IsGetObjectDataVirtual(Type typeToMock)
at Moq.Extensions.IsSerializableMockable(Type typeToMock)
at Moq.SerializableTypesValueProvider.ProvideDefault(MethodInfo member)
at Moq.Mock.GetInitialValue(IDefaultValueProvider valueProvider, Stack`1 mockedTypesStack, PropertyInfo property)
at Moq.Mock.SetupAllProperties(Mock mock, Stack`1 mockedTypesStack)
at Moq.Mock.<>c__DisplayClass72_0.<SetupAllProperties>b__0()
at Moq.PexProtector.Invoke(Action action)
at Moq.Mock.SetupAllProperties(Mock mock)
at Moq.QueryableMockExtensions.FluentMock[T,TResult](Mock`1 mock, Expression`1 setup)
at lambda_method(Closure )
at Moq.Mock.GetInterceptor(Expression fluentExpression, Mock mock)
at Moq.Mock.<>c__DisplayClass66_0`2.<SetupGet>b__0()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.SetupGet[T,TProperty](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock.<>c__DisplayClass65_0`2.<Setup>b__0()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.Setup[T,TResult](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock`1.Setup[TResult](Expression`1 expression)

我认为我没有正确构建测试,但敏锐的眼光可以为我指明正确的方向。

最佳答案

对于遇到类似问题的任何人,以下是我为使其正常工作所做的工作 -

ControllerTest.cs

    [TestMethod]
    public async Task Upload_document_should_upload_document_and_return_dto()
    {
        var goalId = Guid.NewGuid();
        var file = new Services.DTO.FileUploadDto { Id = goalId, Name = "dummy.txt", Uri = "path/to/file" };

        this.goalService.Setup(m => m.UploadDocument(It.IsAny<Guid>(), It.IsAny<IFormFile>(), It.IsAny<string>())).ReturnsAsync(file);

        //**This is the interesting bit**
        this.controller.ControllerContext = this.RequestWithFile();
        var result = await controller.UploadGoalDocument(goalId);

        Assert.IsNotNull(result);
        Assert.AreEqual(file.Id, result.Data.Id);
        Assert.AreEqual(file.Name, result.Data.Name);
        Assert.AreEqual(file.Uri, result.Data.Uri);
    }

    //Add the file in the underlying request object.
    private ControllerContext RequestWithFile()
    {
        var httpContext = new DefaultHttpContext();
        httpContext.Request.Headers.Add("Content-Type", "multipart/form-data");
        var file = new FormFile(new MemoryStream(Encoding.UTF8.GetBytes("This is a dummy file")), 0, 0, "Data", "dummy.txt");
        httpContext.Request.Form = new FormCollection(new Dictionary<string, StringValues>(), new FormFileCollection { file });
        var actx = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());
        return new ControllerContext(actx);
    }

关于c# - 使用 Moq .net Core 对文件上传进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40860305/

相关文章:

c# - 快速创建对象而不是 Activator.CreateInstance(type)

c# - 桌面 Java 应用程序替代方案

c# - 将条件存储为变量

c# - 无返回值的条件运算符

c# - 从代码在存储过程中将数据类型 nvarchar 转换为 datetime 时出错

.net - 异步上传 Azure Blob : BeginUploadFromStream vs. BackgroundWorker

c# - 使用 LINQ 按频率和值排序

javascript - 使用 Jest 对 redux 异步函数进行单元测试

angular - 在 Angular 7 中模拟本地存储

java - JUnit 测试对象的静态计数器