Home > Mobile >  Xunit Unit Testing for Dot Net core application
Xunit Unit Testing for Dot Net core application

Time:09-15

I recently started learning Unit Testing and now have the requirement write unit tests using Xunit and Moq for dot net core application.

I can write some very basic but when it comes to write them for complex classes , I am kind of stuck.

Below is the class I will be writing tests for.

 public class AgeCategoryRequestHandler : IInventoryRequestHandler<InventoryRequest, HandlerResult>
{
    private readonly IRepositoryResolver _repositoryResolver;
    Hotels.HBSI.Logging.ILogger logger;
    public AgeCategoryRequestHandler(IRepositoryResolver repositoryResolver, Hotels.HBSI.Logging.ILogger iLogger)
    {
        _repositoryResolver = repositoryResolver;
        logger = iLogger;
    }

    public async Task<HandlerResult> Run(InventoryRequest inventoryRequest)
    {
        var result = await ProcessRequest(inventoryRequest);
        return CreateResponse(inventoryRequest, result);
    }

    private async Task<int> ProcessRequest(InventoryRequest inventoryRequest)
    {
        logger.Info("AgeCategory requesthandler processrequest start");
        var repository = _repositoryResolver.ResolveEstabAgeCategory();
        if (repository is not null)
        {
            return await repository.InsertUpdateEstabAgeCategoryDetail(inventoryRequest.EstabAgeCategories)
           .ConfigureAwait(false);
        }
        logger.Info("AgeCategory requesthandler processrequest complete");
        return InernalError.reponotfound;
    }
    public HandlerResult CreateResponse(InventoryRequest inventoryRequest, int resultCount)
    {
        var requestCount = inventoryRequest.EstabAgeCategories.Count;
        var handlerResult = new HandlerResult() { Id = RequestHandlerEnum.AgeCategrory.ToInt() };
        if (requestCount > 0 && resultCount < requestCount)
        {
            handlerResult.IsSuccess = false;
            handlerResult.ErrorCode = OTAErrorType.InvalidAgeCategory.ToInt();
        }
        else if (requestCount > 0 || requestCount == resultCount)
        {
            handlerResult.IsSuccess = true;
            handlerResult.ErrorCode = 0;
        }
        return handlerResult;
    }
}

Just to start , IRepositoryResolver and ILogger are in the constructor so I have created mock for these but unable to go beyond that as I am still in initial phase of learning.

Could someone explain me the steps/approach to accomplish this?.

Edit : What I have done so far is below ( can't figure out what are the things to be done and where to start or write )

Edit 2 : Did some more modifications to my test code , can someone comment if I am in right direction ? what else can I test ?

 public class AgeCategoryRequestHandlerTest
{
    private AgeCategoryRequestHandler _ageCategoryRequestHandler;

    private readonly Mock<AgeCategoryRequestHandler> _ageCategory = new Mock<AgeCategoryRequestHandler>();

    private readonly Mock<Hotels.HBSI.Logging.ILogger> _mockLogger = new Mock<Hotels.HBSI.Logging.ILogger>();
    private readonly Mock<IRepositoryResolver> _mockRepositoryResolver = new Mock<IRepositoryResolver>();
    public AgeCategoryRequestHandlerTest()
    {

        _ageCategoryRequestHandler = new AgeCategoryRequestHandler(_mockRepositoryResolver.Object, _mockLogger.Object);


    }


    [Fact]


   public async void  Testtt()

    {
        var fixture = new Fixture();
        var inventory = fixture.Create<InventoryRequest>();
        var hndlr = fixture.Create<HandlerResult>();

        hndlr.ErrorCode = 0;
        int resultCount = 3;

        await _ageCategoryRequestHandler.Run(inventory);

        HandlerResult response =   _ageCategoryRequestHandler.CreateResponse(inventory, resultCount);

        Assert.Equal(hndlr.ErrorCode, response.ErrorCode);

     
    }

CodePudding user response:

From the unit test code you've posted, it looks like you are getting confused on what to test.

Look at your class and identify your "public" interface i.e. what methods can be called from other parts of your code. You should really only test public methods. Private methods are usually tested via public methods.

Looking at AgeCategoryRequestHandler, you have two public methods - Run and CreateResponse. I would question whether CreateResponse needs to be public but we'll leave it for now. For each of these methods, you want to be asserting that the returned value is what you expect given the input value.

private AgeCategoryRequestHandler _ageCategoryRequestHandler;

// Not needed
private readonly Mock<AgeCategoryRequestHandler> _ageCategory = new Mock<AgeCategoryRequestHandler>();

private readonly Mock<Hotels.HBSI.Logging.ILogger> _mockLogger = new Mock<Hotels.HBSI.Logging.ILogger>();
private readonly Mock<IRepositoryResolver> _mockRepositoryResolver = new Mock<IRepositoryResolver>();

public AgeCategoryRequestHandlerTest()
{
    _ageCategoryRequestHandler = new AgeCategoryRequestHandler(_mockRepositoryResolver.Object, _mockLogger.Object);
}

The set up of the unit test is going the right way - you have created mocks for your dependencies but I see you have created a mock for the class you are trying to test - this is not needed and can be removed. You want to be testing the actual class itself which you are initializing in the constructor.

public async Task<HandlerResult> Run(InventoryRequest inventoryRequest)
{
    var result = await ProcessRequest(inventoryRequest);
    return CreateResponse(inventoryRequest, result);
}

private async Task<int> ProcessRequest(InventoryRequest inventoryRequest)
{
   _logger.LogInformation("AgeCategory requesthandler processrequest start");

    var repository = _repositoryResolver.ResolveEstabAgeCategory();

    if (repository != null)
    {
        return await repository.InsertUpdateEstabAgeCategoryDetail(inventoryRequest.EstabAgeCategories).ConfigureAwait(false);
    }

    _logger.LogInformation("AgeCategory requesthandler processrequest complete");

    return 0;
}

We can test the public Run method by looking at the method and seeing what it is going to do when executed. Firstly, it's going to call a private method ProcessRequest. Inside ProcessRequest, the IRepositoryResolver dependency is going to be used. This means we need to "set up" this dependency in our unit test to satisfy the if (repository != null) condition.

I assume the IRepositoryResolver returns another interface (?) - something like:

public interface IRepository
{
    Task<int> InsertUpdateEstabAgeCategoryDetail(List<int> x);
}

So in your unit test, you need to create a mock for the repository being returned from IRepositoryResolver:

private readonly Mock<IRepository> _mockRepository = new Mock<IRepository>();

Then, you need to set up the mock IRepositoryResolver to return the mock repository above:

_mockRepositoryResolver
    .Setup(x => x.ResolveEstabAgeCategory())
    .Returns(_mockRepository.Object);

This is to satisfy the if (repository != null) condition.

_mockRepository
    .Setup(x => x.InsertUpdateEstabAgeCategoryDetail(inventoryRequest.EstabAgeCategories))
    .ReturnsAsync(6);

Next, you need to set up the InsertUpdateEstabAgeCategoryDetail() method on the mock repository to return a value. This value is being returned by ProcessRequest() and then used to call CreateResponse(inventoryRequest, result) as the result parameter.

if (requestCount > 0 && resultCount < requestCount)
{
    handlerResult.IsSuccess = false;
    handlerResult.ErrorCode = (int)OTAErrorType.InvalidAgeCategory;
}
else if (requestCount > 0 || requestCount == resultCount)
{
    handlerResult.IsSuccess = true;
    handlerResult.ErrorCode = 0;
}

Now you can look at the CreateResponse method and by setting different values for inventoryRequest.EstabAgeCategories and setting up the mock _mockRepository.Setup(x => x.InsertUpdateEstabAgeCategoryDetail(inventoryRequest.EstabAgeCategories)).ReturnsAsync(6); to return different values, you can satisfy the different paths through the if statement.

CreateResponse is returning an instance of HandlerResult which in turn is being returned by Task<HandlerResult> Run. This is the returned object you want to make assertions on.

One of the unit test cases might look like this (I have not tested it myself):

[Fact]
public async Task GivenInventoryRequest_WhenRun_ThenHandlerResultReturned()
{
    // arrange
    var inventoryRequest = new InventoryRequest
    {
        EstabAgeCategories = new List<int>
        {
            1, 2, 3, 4, 5
        }
    };

    _mockRepository
        .Setup(x => x.InsertUpdateEstabAgeCategoryDetail(inventoryRequest.EstabAgeCategories))
        .ReturnsAsync(6);

    _mockRepositoryResolver
        .Setup(x => x.ResolveEstabAgeCategory())
        .Returns(_mockRepository.Object);

    // act
    var result = await _ageCategoryRequestHandler.Run(inventoryRequest);

    // assert
    _mockRepository
        .Verify(x => x.InsertUpdateEstabAgeCategoryDetail(inventoryRequest.EstabAgeCategories), Times.Once);
    
    Assert.True(result.Id == 0);
    Assert.True(result.ErrorCode == 0);
    Assert.False(result.IsSuccess);
}
  • Related