2

I have created a relatively simple Controller as follows:

[Route("api/areas")]
    public class AreasController : Controller
    {
        private IAreaRepository _areaRepository;
        private ILogger<AreasController> _logger;

        // Constructor.
        public AreasController(
            IAreaRepository areaRepository,
            ILogger<AreasController> logger
        )
        {
            _areaRepository = areaRepository;
            _logger = logger;
        }

        [HttpGet()]
        public IActionResult GetAreas()
        {
            try
            {
                _logger.LogTrace("AreasController.GetAreas called.");

                // Create an IEnumerable of Area objects by calling the repository.
                var areasFromRepo = _areaRepository.GetAreas();

                var areas = Mapper.Map<IEnumerable<AreaDto>>(areasFromRepo);

                // Return a code 200 'OK' along with an IEnumerable of AreaDto objects mapped from the Area entities.
                return Ok(areas);

            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to get all Areas: {ex}");

                return BadRequest("Error Occurred");
            }

        }
...

I am new to Unit Testing and am struggling to get the most basic of tests working. I am using XUnit and Moq in Visual Studio 2017.

To dip my toe in the water I wanted to test that the GetAreas method on the controller, if there were some Areas, would return an okObjectResult, but it doesn't!

Here's my test:

[Fact]
        public void ReturnAreasForGetAreas()
        {
            //Arrange
            var area = new Area
            {
                Id = new Guid("761317f6-f9d7-4fa4-a8fe-c6179daee3da"),
                Description = "Test Area",
                SortIndex = 1
            };

            var _mockAreaRepository = new Mock<IAreaRepository>();
            _mockAreaRepository
                .Setup(x => x.GetAreas())
                .Returns(new List<Area> { area });

            var _mockLogger = new Mock<ILogger<AreasController>>();
            var _sut = new AreasController(_mockAreaRepository.Object, _mockLogger.Object);

            // Act
            var result = _sut.GetAreas();
            Assert.NotNull(result);

            // Assert
            var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
        }

Let me know if you want any Repository or Entity details. I am expecting the problem to be in my misunderstanding of setting up the mocked objects, either way I can't see it.

14
  • 1
    Debug and check what type is actually being return. Then you can see if you are checking for the wrong type. possible error is being thrown and the bad request is being returned. Commented Jan 19, 2018 at 15:46
  • I'm getting a BadRequest object back with a StatusCode 400. Commented Jan 19, 2018 at 15:49
  • 1
    @TDC you should be injecting IMapper so as to be able to mock that as well. Commented Jan 19, 2018 at 15:52
  • 1
    For what it's worth, you should avoid wrapping huge blocks of code in a single try...catch block, especially when catching something as generic as Exception. It doesn't really help you find the issue since literally anything there could have thrown an exception of some sort or another. Either catch discrete units (i.e. try..catch repository access, then try..catch mapping) or catch and handle exception types explicitly. Commented Jan 19, 2018 at 19:54
  • 1
    Also, in general, you should never catch Exception unless you combine it with throw; in the catch block, i.e. rethrow the exception (usually done simply for logging purposes). Only specific exceptions should be swallowed. Commented Jan 19, 2018 at 19:57

1 Answer 1

1

Debug and check what type is actually being return. Then you can see if you are checking for the wrong type. It is possible an error is being thrown and the bad request is being returned.

another way to ensure that in the test

// Act
var result = _sut.GetAreas() as OkObjectResult;

//Assert
Assert.NotNull(result);

If using Automapper then I would also advise injecting the IMapper into the controller to be able to mock that as well when testing to avoid having to set it up just for tests. Tightly coupling to static dependencies can have undesired effects on the testability of your code.

[Route("api/areas")]
public class AreasController : Controller {
    private IAreaRepository _areaRepository;
    private ILogger<AreasController> _logger;
    private IMapper mapper;

    // Constructor.
    public AreasController(
        IAreaRepository areaRepository,
        ILogger<AreasController> logger,
        IMapper mapper
    ) {
        _areaRepository = areaRepository;
        _logger = logger;
        this.mapper = mapper;
    }

    [HttpGet()]
    public IActionResult GetAreas() {
        try {
            _logger.LogTrace("AreasController.GetAreas called.");
            // Create an IEnumerable of Area objects by calling the repository.
            var areasFromRepo = _areaRepository.GetAreas();
            var areas = mapper.Map<IEnumerable<AreaDto>>(areasFromRepo);
            // Return a code 200 'OK' along with an IEnumerable of AreaDto objects mapped from the Area entities.
            return Ok(areas);
        } catch (Exception ex) {
            _logger.LogError($"Failed to get all Areas: {ex}");
            return BadRequest("Error Occurred");
        }
    }
    //...
}

just make sure to associate the abstraction with its implementation at the composition root.

services.AddAutoMapper(assembly1, assembly2 /*, ...*/);

Reference AutoMapper extensions for Microsoft.Extensions.DependencyInjection

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.