简体   繁体   English

使用Moq .net Core对fileupload进行单元测试

[英]Unit testing fileupload with Moq .net Core

I have a method in WebApi controller that I want to write unit tests for. 我在WebApi控制器中有一个方法,我想编写单元测试。 This is how my controller method looks: 这是我的控制器方法的样子:

Controller.cs 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});
    }

How can I mock the request object in unit testing? 如何在单元测试中模拟请求对象? I tried following but ran into problems with IFormFileCollection. 我尝试了以下但遇到了IFormFileCollection的问题。 The following line throws error: 以下行抛出错误:

system.argumentexception interface not found 找不到system.argumentexception接口

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

ControllerTest.cs 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
            }
        }
    }

Detailed stack trace: 详细的堆栈跟踪:

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)

I am thinking I have not constructed the test properly, but a keen eye can point me in the right direction. 我想我没有正确地构建测试,但敏锐的眼光可以指向正确的方向。

For anyone facing similar problem, here's what I did to get it working - 对于任何面临类似问题的人来说,这就是我为使其工作所做的一切 -

ControllerTest.cs 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);
    }

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

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