Search code examples
c#asp.net-web-apifakeiteasy

Unable to set Return values with FakeItEasy


I am just working on testing some Web Api controllers. However, I cannot seem to find what is the problem with being able to set the return values to be a list. When debugging, the output of ClassSql.ReadAllClassesAsync() is not being faked for some odd reason. If I test a the method ClassSql.ReadClassAsync() that only returns a single value, the faking works perfectly. Hence, all my tests work.

The controller is:

    public class ClassController : ControllerBase
    {
        private readonly IClassSqlService ClassSql;
        private readonly ILogger<ClassController> Logger;

        public ClassController(
                IConfiguration config,
                ILogger<ClassController> logger,
                IDataAccessAbstractFactory<IClassSqlService> classSql)
        {
            string SMString = config.GetValue<string>(DataAccessName.SMCnx)!;
            Logger = logger;
            ClassSql = classSql.Create(SMString);
        }

        [HttpGet]
        [ProducesResponseType(200, Type = typeof(List<ClassModel>))]
        public async Task<ActionResult<List<ClassModel>>> ReadAllClasses(
                string? academic_year = null)
        {
            List<ClassModel> output;

            if(academic_year is not null)
            {
                bool isIdValid = int.TryParse(academic_year, out int year);

                if (isIdValid is false)
                    return BadRequest();

                output = await ClassSql.ReadAllClassesByAcademicYearAsync(academic_year);
            }
            else
            {
                output = await ClassSql.ReadAllClassesAsync();
            }

            if (output.Any() is false)
                return NotFound();

            return Ok(output);
        }
}

My unit testing is as follows:

public class ClassControllerTests
{
    private readonly IClassSqlService ClassSql;
    private readonly ClassController sut;

    public ClassControllerTests()
    {
        var config = A.Fake<IConfiguration>();
        var classSql = A.Fake<IDataAccessAbstractFactory<IClassSqlService>>();
        var logger = A.Fake<ILogger<ClassController>>();

        sut = new ClassController(config, logger, classSql);
        ClassSql = classSql.Create(string.Empty);
    }

    [Fact]
    public async Task ReadAllClassesShouldReturnOk()
    {
        // arrange
        var classes = A.CollectionOfFake<ClassModel>(5);
        A.CallTo(() => ClassSql.ReadAllClassesAsync()).Returns(classes.ToList());

        // action
        var result = await sut.ReadAllClasses();

        // assert
        result.Result.Should().NotBeNull();
        result.Result.Should().BeOfType(typeof(OkObjectResult));
        result.Value.Should().NotBeEmpty();
    }
}

I have read the documentation of FakeItEasy and followed their example with faking collections but no success.

Any help would be amazing.

Thank you.


Solution

  • When debugging, the output of the controller is not being faked for some odd reason

    Can you explain what this means? Also, it's not clear what result you do get. Which of the assertions at the end of the test fail? How?

    I'm not sure what the difference between your single-value and multi-value returning tests (or production methods) is, but I'll note one thing. I suspect it's your problem.

    In your test code, you create a fake IClassSqlService called ClassSql (in a sort of roundabout way, by calling Create on a fake IDataAccessAbstractFactory<IClassSqlService>).

    Inside ClassController, classSql.Create(SMString) is called to make a different IClassSqlService called ClassSql.

    The test configures the first ClassSql to return classes.ToList(). The controller calls the second ClassSql whose unconfigured ReadAllClassesAsync, I'm guessing, returns a fake list of ClassModels, and it acts like it's empty.