Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing fileupload with Moq .net Core

I have a method in WebApi controller that I want to write unit tests for. This is how my controller method looks:

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. The following line throws error:

system.argumentexception interface not found

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
            }
        }
    }

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.

like image 252
user869375 Avatar asked Nov 29 '16 07:11

user869375


1 Answers

For anyone facing similar problem, here's what I did to get it working -

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);
    }
like image 192
user869375 Avatar answered Oct 01 '22 13:10

user869375