简体   繁体   中英

Faking a generic method call in C# is not returning the correct object with FakeItEasy

I have this method I need to test

    public async Task<IActionResult> Completed(string nprUserId, [FromBody] DateRangeDto model)
    {
        var result = await _geAppService.Completed<ExpandoObject>(nprUserId, model.StartDate, model.EndDate);
        var r = result as OperationResult;

        if (r == null)
        {
            // There is no error.  Return JSON
            return Json(result, camelCaseOption);
        }
        else
        {
            // An error occurred.  Return a 404 and include the error message
            return NotFound(r.ErrorMessage);
        }
    }

This is my test

    [Fact]
    [Trait("Controller", "Completed")]
    public async Task Completed_WhenRIsNotNull_ReturnNotFoundWithMessage()
    {
        //arrange
        string nprUserId = string.Empty;
        var model = CreateRandomDateRangeDto();
        OperationResult r = CreateRandomOperationResult();
        var startDate = DateTime.Now;
        var endDate = DateTime.Now;

        //I tried this
        A.CallTo(nprAppService)
            .Where(call => call.Method.Name == "Completed")
            .WithReturnType<OperationResult>()
            .Returns(r);
       //I tried this as well
        A.CallTo(() => nprAppService.Completed<ExpandoObject>(nprUserId, startDate, endDate))
            .Returns(r);

        //act
        var result = await controller.Completed(nprUserId, model);

        // Assert
        A.CallTo(() => nprAppService.Completed<ExpandoObject>(nprUserId, startDate, endDate))
            .MustHaveHappened();
        result.Should().BeOfType<NotFoundObjectResult>()
            .Subject.Value.Should().BeOfType<string>();
    }

When the sut : controller gets called, the Complete generic method returns an object instead of an OperationResult.

How can I solve this issue?

Assuming CreateRandomDateRangeDto returns the model with StartDate and EndDate set to DateTime.Now ,

Your test should be using model.StartDate and model.EndDate instead of startDate , and endDate in the test method. because their values will be different.

 [Fact]
 [Trait("Controller", "Completed")]
 public async Task Completed_WhenRIsNotNull_ReturnNotFoundWithMessage()
 {
     //arrange
     string nprUserId = string.Empty;
     var model = CreateRandomDateRangeDto();
     OperationResult r = CreateRandomOperationResult();

     A.CallTo(() => nprAppService.Completed<ExpandoObject>(nprUserId, model.StartDate, model.EndDate))
        .Returns(r);

    //act
    var result = await controller.Completed(nprUserId, model);

    // Assert
    A.CallTo(() => nprAppService.Completed<ExpandoObject>(nprUserId, model.StartDate, model.EndDate))
        .MustHaveHappened();
    result.Should().BeOfType<NotFoundObjectResult>()
        .Subject.Value.Should().BeOfType<string>();
}

Update : Further explanation

For the first approach of configuring through passing the fake object, the return type should include Task .

A.CallTo(nprAppService)
    .Where(call => call.Method.Name == "Completed")
    .WithReturnType<Task<OperationResult>>()
    .Returns(r)

The second approach requires the exact value/object in order to return the specified object. That's why the test only works with model.EndDate and model.StartDate

If you didn't care about the start and end date values matching, you can...

A.CallTo(() => nprAppService.Completed<ExpandoObject>(nprUserId, A<DateTime>._, A<DateTime>._))
                .Returns(r);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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