Search code examples
c#unit-testingwebassembly

NET 7 C# Controller test does not return Success Code when ForEach loop is included in MediatR RequestHandler class


In my wasm c# application, I'm trying to test my ContactController. In the ContactControllerTests class, I have a test named ReturnsSuccessResult(). However, when I run a test for ReturnsSuccessResult(), the test crashes with an error 500 (Internal Server Error).

I'm using a view model class, ContactListVm, a query class, GetContactListquery.cs, and a query handler, GetContactListQueryHandler.cs.

You will see in the GetContactListQueryHandler.cs that a loop is getting and mapping a state ID and state abbreviation to the view model. I think this causes the error because, if I remove the loop then controller unit test is successful.

Can you tell me what I'm missing or doing incorrectly when testing the ContactController?

ContactListVm

public class ContactListVm
{
  public Guid ContactId { get; set; }
  public string FirstName { get; set; } = string. Empty;
  public string LastName { get; set; } = string. Empty;
  public string NameSuffix {get; set;} = string. Empty;
  public string? MiddleName { get; set; }
  public string? CompanyName { get; set; }
  public string? Title { get; set; }
  public string Address1 { get; set; } = string. Empty;
  public string? Address2 { get; set; }
  public string? Address3 { get; set; }
  public string City { get; set; } = string. Empty;
  public Guid StateId { get; set; }
  public string? StateAbbreviation { get; set; }
  public string ZipCode { get; set; } = string. Empty;
  public string? Email { get; set; }
  public string PhoneNumber { get; set; } = string. Empty;
  public string? Fax { get; set; }
}

GetContactListQuery

public class GetContactListQuery : IRequest<List<ContactListVm>>
{
    
}

GetContactListQueryHandler

public GetContactListQueryHandler(IMapper mapper, IContactRepository contactRepository,
        IStateRepository stateRepository)
{
        _mapper = mapper;
        _contactRepository = contactRepository;
        _stateRepository = stateRepository;
}
public async Task<List<ContactListVm>> Handle(GetContactListQuery request, CancellationToken 
        cancellationToken)
{
        var allContacts = (await _contactRepository.ListAllAsync())
            .OrderBy(c => c.LastName);

        var newContactList = new List<ContactListVm>();

        foreach (var contact in allContacts)  // Error goes away if refactored to remove this foreach
        {
            var contactDetailDto = _mapper.Map<ContactListVm>(contact);
            if (contactDetailDto.StateId != Guid.Empty)
            {
                var state = await _stateRepository.GetByIdAsync(contactDetailDto.StateId);
                contactDetailDto = _mapper.Map(state, contactDetailDto);
            }
            newContactList.Add(contactDetailDto);
        }

  return newContactList;
}

Contact Controller

[Route("api/[controller]")]
[ApiController]
public class ContactController : ControllerBase
{
private readonly IMediator _mediator;

public ContactController(IMediator mediator)
{
  _mediator = mediator;
}

[HttpGet("all", Name ="GetAllContacts")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<List<ContactListVm>>> GetAllContacts()
{
   var dtos = await _mediator.Send(new GetContactListQuery());
   return Ok(dtos);
}

[HttpGet("{id}", Name = "GetContactById")]
public async Task<ActionResult<ContactDetailVm>> GetContactById(Guid id)
{
    var getContactDetailQuery = new GetContactDetailQuery() { Id = id };
    return Ok(await _mediator.Send(getContactDetailQuery));
}

[HttpPost(Name = "AddContact")]
public async Task<ActionResult<CreateContactCommandResponse>> Create
            ([FromBody] CreateContactCommandResponse createContactCommand)
{
            var response = await _mediator.Send(createContactCommand);
            return Ok(response);
}
}

**Contact Controller Tests**

[Collection("Controllers Test Collection")]
public class ContactControllerTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
 private readonly CustomWebApplicationFactory<Program> _factory;
 private readonly Mock<IMediator> _mediatorMock;
 private readonly ContactController _contactController;
    
 public ContactControllerTests(CustomWebApplicationFactory<Program> factory)
{
    _factory = factory;
    _mediatorMock = new Mock<IMediator>();
    _contactController = new ContactController(_mediatorMock.Object);
}
    
[Fact]
public async Task ReturnsSuccessResult()    //<-- 500 Internal Server Error in this test
{
            var client = _factory.GetAnonymousClient();
    
            var response = await client.GetAsync("/api/contact/all");
    
            response.EnsureSuccessStatusCode();
    
            var responseString = await response.Content.ReadAsStringAsync();
    
            var result = JsonSerializer.Deserialize<List<ContactListVm>>(responseString);
    
            Assert.IsType<List<ContactListVm>>(result);
            Assert.NotEmpty(result);
}

GetContactListQueryHandlerTests

    public class GetContactListQueryHandlerTests
{
    private readonly IMapper _mapper;
    private readonly Mock<IContactRepository> _mockContactRepository;
    private readonly Mock<IStateRepository> _mockStateRepository;

    public GetContactListQueryHandlerTests()
    {
        var contactRepositoryMock = new ContactRepositoryMock();
        _mockContactRepository = contactRepositoryMock.GetContactRepositoryMock();

        var stateRepositoryMock = new StateRepositoryMock();
        _mockStateRepository = stateRepositoryMock.GetStateRepositoryMock();

        var configurationProvider = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile<ContactMappingProfile>();
            cfg.AddProfile<StateMappingProfile>();
        });
        _mapper = configurationProvider.CreateMapper();
    }

    [Fact]
    public async Task GetContactList_Result_IsOfTypeListContactListVm()
    {
        var handler = new GetContactListQueryHandler(_mapper,
            _mockContactRepository.Object, _mockStateRepository.Object);

        var result = await handler.Handle(new GetContactListQuery(),
            CancellationToken.None);

        result.ShouldBeOfType<List<ContactListVm>>();
    }
}

Solution

  • The back-and-forth discussion with DSN, led me to look closely at the ReturnsSuccessResult() in the ContactControllerTests class. That class inherits from ICLassFixture<CustomWebApplicationFactory>. The ReturnsSuccessResult() method populates var client = _factory.GetAnonymousClient(); The _factory.GetAnonymousClient() method is defined within the CustomWebApplicationFactory class which, in turn, uses EntityFramework to access the dbContext.

    Therefore, the ContactController ReturnsSuccessResult() method uses the actual SQL Server database rather than the mocked database which is used by the integration unit tests.

    On a hunch that the problem was actually due to the database, I deleted the database via the SQL Object Explorer, deleted the Migrations folder in the Persistence project of my application and then regenerated the database.

    After that, the ContactControllerTests ReturnsSuccessResult() method ran successfully. Something was out of sync, but I am not sure exactly what.

    Thanks to DSN.