2

The ContentResult I am trying to check in unit test of the getMethod(string id) is always returning null. The Service called by controller returns DTO Model.

I have tried actionresult as OkNegotiatedContentResult actionresult as OkNegotiatedContentResult

Unit Test:

      [TestMethod]
      public void GetUserInformation_ReturnsUserDetails()
        {
            //Arrange
               UserDetails userDetails = new UserDetails();
               mockUserService.Setup(x => 
         x.GetuserInformation(It.IsAny<string> 
          ())).Returns(userDetails);
             //Act
             IHttpActionResult result = 
              userController.GetUserInformation("226");
              var contentResult = result as 
             OkNegotiatedContentResult<UserDetails>;
            //Assert
           //Validate Ok Result
              Assert.IsNotNull(contentResult);//this fails
              Assert.IsNotNull(contentResult.Content);
              Assert.AreEqual("226", contentResult.Content.userID);
              Assert.IsInstanceOfType(result, 
              typeof(OkNegotiatedContentResult<UserDetails>));//this assert works

            }

Controller:

public IHttpActionResult GetUserInformation(string userID)
        {
            if (!string.IsNullOrEmpty(userID))
            {
                var result = _userService.GetUserInformation(userID);
                if (result != null)
                {
                    return Ok(result);
                }
                return InternalServerError();
            }
            else
            {
                return BadRequest("User Not Found");
            }
        }

Service Layer:

 public UserDetails GetUserInformation(string userID)
        {
            UserDetails userDetails = new UserDetails();
            //business logic
            return userDetails;
        }

ContentNegotiator = '((System.Web.Http.Results.OkNegotiatedContentResult)result).ContentNegotiator' threw an exception of type 'System.InvalidOperationException'

Request = '(result as OkNegotiatedContentResult).Request' threw an exception of type 'System.InvalidOperationException'

ps_dev
  • 69
  • 1
  • 9
  • Actually it is not a unit testing. You shouldn't call controllers to test services. And there shouldn't be any business logic in controller. All the logic should stay in the service. – Vlad DX Aug 21 '19 at 19:57
  • No, if you don't have any business logic in them. And you shouldn't have any. – Vlad DX Aug 21 '19 at 21:04
  • You test the service without mocking it. But everything what is injected to the service through it's constructor should be mocked. – Vlad DX Aug 21 '19 at 21:32
  • Trying to find a good article. Everywhere people are testing controllers. But it's just a waste of time. You better focus on the services. The article about thin controllers: https://adamtibi.net/06-2013/you-should-unit-test-your-controller-not – Vlad DX Aug 21 '19 at 21:42
  • @VladimirSerykh Is it possible to do Unit Test for a Service when it does not have an interface? – ps_dev Aug 26 '19 at 16:55
  • You can try to Mock it. But better to have an interface. If both options don't fit, then you can create child class that does nothing. But it's not the best way of doing this. – Vlad DX Aug 27 '19 at 10:52
  • Do you have any idea of which framework actually can mock out non-interface/virtual methods in a clean way? – ps_dev Aug 27 '19 at 23:51
  • Honestly, I'm not sure. I'm using `Moq`. But it can't do this because it's just implementing interface or creating derived class on-the-fly. Just found an answer that could be helpful: https://stackoverflow.com/a/11738166/3503521 But I encourage you or authors of the services to start using interfaces. SOLID principles really help to write better code. – Vlad DX Aug 28 '19 at 08:12
  • Thank you so much! – ps_dev Aug 28 '19 at 16:57

2 Answers2

1

There could be different approaches to build an API application.

My approach is that the API should be build of 3 layers:

  • Presentation layer: controllers
  • Business logic: services
  • Data layer: repositories

Controllers and repositories should be as thin as possible. All the logic should be in service layer. In that case application would be maintainable, extendable, and testable.

Test example

Packages used:

  • NUnit
  • NUnit3TestAdapter
  • Moq

You can also use:

  • FluentAssertions
  • AutoFixture.NUnit3

Example of test class for a service:

[TestFixture]
public class OrdersServiceTests
{
    private OrdersService _sut;

    private Mock<ICapacityService> _capacityService;

    private Mock<IOrdersRepository> _ordersRepository;

    [SetUp]
    public void Setup()
    {
        // Executed for each test. Mocks are recreated. Tests are not influencing each other.

        _ordersRepository = new Mock<IOrdersRepository>();
        _capacityService = new Mock<ICapacityService>();

        _sut = new OrdersService(_ordersRepository.Object, _capacityService.Object);
    }

    [Test]
    public async Task Should_CheckCapacity_WhenCreatingOrder()
    {
        //// Arrange

        var orderEntity = new OrderEntity
        {
            Id = Guid.NewGuid(),
        };

        //// Act

        await _sut.CreateAsync(orderEntity);

        //// Assert

        _capacityService
            .Verify(
                x => x.CheckCapacity(
                    It.Is<Guid>(g => g.Equals(orderEntity.Id)))),
                Times.Once);
    }
}
Vlad DX
  • 4,200
  • 19
  • 28
1

I had the same problem, and it turns out I forgot to call Callbase = true on the controller Mock.

deHaar
  • 17,687
  • 10
  • 38
  • 51
H.Salah
  • 11
  • 1