Search code examples
c#unit-testingnsubstituteautofixture

Unit test result getting null due to FirstOrDefault predicate


I am using autofixture, nsubstitude and fluent assertions in my unit tests.

This is my main code:

 public async Task<ServiceResponse<PlanInventoryDto>> Update(PlanInventoryDto planInventoryDto)
        {
            planInventoryDto.ThrowIfNull(nameof(planInventoryDto));

            if (planInventoryDto == null)
            {
                return serviceResponseHelper.SetError<PlanInventoryDto>(null, localize["InvalidModel"], StatusCodes.Status400BadRequest, true);
            }

            var partProdArea = repository.GetQueryable().FirstOrDefault(predicate: q => q.Id == planInventoryDto.Id);

            if (partProdArea != null)

            {
                var mappingResult = mapper.Map(planInventoryDto, partProdArea);

                await repository.UpdateAsync(mappingResult).ConfigureAwait(false);

                var result = repository.GetFirstOrDefault(predicate: p => p.Id == planInventoryDto.Id);

                var dto = mapper.Map<PlanInventory, PlanInventoryDto>(result);

                return serviceResponseHelper.SetSuccess(dto);

            }

            return serviceResponseHelper.SetError<PlanInventoryDto>(null, localize["ModelIsNotFound"], StatusCodes.Status400BadRequest, true);
        }

And this is my unit test:

[Fact]
        [Trait("Operation", "Update")]
        [Trait("Category", "PlanInventory")]
        public async Task PlanInventoryService_Update_ReturnsSuccessResponse()
        {
            // Arrange
            var planInventoryDto = _fixture.Create<PlanInventoryDto>();
            var planInventory = _fixture.Build<Domain.PlanProcess.PlanInventory>()
                .With(p => p.Id, planInventoryDto.Id)
                .Create();
            var updatedPlanInventory = _fixture.Create<Domain.PlanProcess.PlanInventory>();
            var updatedDto = _fixture.Create<PlanInventoryDto>();

            _repository.GetFirstOrDefault(Arg.Any<Expression<Func<Domain.PlanProcess.PlanInventory, bool>>>(),
                Arg.Any<Func<IQueryable<Domain.PlanProcess.PlanInventory>, IOrderedQueryable<Domain.PlanProcess.PlanInventory>>>(),
                Arg.Any<Func<IQueryable<Domain.PlanProcess.PlanInventory>, IIncludableQueryable<Domain.PlanProcess.PlanInventory, object>>>())
                .Returns(planInventory); 

            _repository.GetFirstOrDefault(Arg.Any<Expression<Func<Domain.PlanProcess.PlanInventory, bool>>>(),
                Arg.Any<Func<IQueryable<Domain.PlanProcess.PlanInventory>, IOrderedQueryable<Domain.PlanProcess.PlanInventory>>>(),
                Arg.Any<Func<IQueryable<Domain.PlanProcess.PlanInventory>, IIncludableQueryable<Domain.PlanProcess.PlanInventory, object>>>())
                .Returns(updatedPlanInventory); 

            _mapper.Map(Arg.Any<PlanInventoryDto>(), Arg.Any<Domain.PlanProcess.PlanInventory>())
                .Returns(updatedPlanInventory);
            _repository.UpdateAsync(Arg.Any<Domain.PlanProcess.PlanInventory>())
                .Returns(Task.FromResult(updatedPlanInventory));
            _mapper.Map<Domain.PlanProcess.PlanInventory, PlanInventoryDto>(updatedPlanInventory)
                .Returns(updatedDto);

            // Act
            var result = await _planInventoryService.Update(planInventoryDto);

            // Assert
            _serviceResponseHelper.Received(1).SetSuccess(Arg.Any<PlanInventoryDto>());
        }

Result is getting null beacause partProdArea variable getting null from main code. I could not figure it out no matter what i have tried. Is there a different writing way of FirstOrDefault that has predicate, i could not figure it out a little help would be very helpful.


Solution

  • As I said in a comment, in your production code, you have: repository.GetQueryable().FirstOrDefault(). However, you are not mocking the GetQueryable() method of your _repository!

    You could create a variable representing the IQueryable that is returned from that method, containing the planInventory, and setup your _repository to return it. This way, you don't need to mock explicitly the .FirstOrDefault method:

        IEnumerable<Order> inventories = new List<Domain.PlanProcess.PlanInventory> { planInventory }; // Create a list with a single element, the planInventory
        IQueryable<Order> inventoriesQuery = inventories.AsQueryable(); // Transform the list in a IQueryable, since that is the type returned by the `GetQueryable()`
        _repository.GetQueryable().Returns(inventoriesQuery); // Tell the mock to return the IQueryable when GetQueryable() is called