Search code examples
c#unit-testingxunitwebapi

How to unit test a Web API controller using XUnit


I am trying to unit test a method within my controller in my Web API using XUnit. The role of the method is to get a single title, by ISBN, from the database. The issue I came across during unit testing is that I am unsure how to insert the dummy data that I must perform the test on, as well as how the Assert function works.

TitleController.cs

[ApiController]
[Route("titlecontroller")]
public class TitleController : Controller
{
    private IGtlTitleRepository _gtlTitleRepository;

    public TitleController(IGtlTitleRepository gtlTitleRepository)
    {
        _gtlTitleRepository = gtlTitleRepository;
    }

    [Route("getTitle/{ISBN}")]
    [HttpGet()]
    public GtlTitle GetTitle(string ISBN)    
    {
        return _gtlTitleRepository.GetTitle(ISBN);
    }
}

IGtlTitleRepository.cs

    public interface IGtlTitleRepository
{
    GtlTitle GetTitle(string ISBN);
}

MockGtlTitleRepository.cs

    public class MockGtlTitleRepository : IGtlTitleRepository
{
    private readonly string _connection;
    public MockGtlTitleRepository(IOptions<ConnectionStringList> connectionStrings)
    {
        _connection = connectionStrings.Value.GTLDatabase;
    }


    private List<GtlTitle> _titleList;

    public GtlTitle GetTitle(string ISBN)
    {
        using (var connection = new SqlConnection(_connection))
        {
            connection.Open();
            return connection.QuerySingle<GtlTitle>("GetTitleByISBN", new { ISBN }, commandType: CommandType.StoredProcedure);
        }
    }

}

Right, as for my test code, I was able to write the following code, but as I said above, I can't figure out a proper way to test the method.

 public class UnitTest1
{
    [Fact]
    public void Test1()
    {

        var repositoryMock = new Mock<IGtlTitleRepository>();
        var title = new GtlTitle();
        repositoryMock.Setup(r => r.GetTitle("978-0-10074-5")).Returns(title);
        var controller = new TitleController(repositoryMock.Object);

        var result = controller.GetTitle("978-0-10074-5");
       // assert??
        repositoryMock.VerifyAll();
    }
}

What should be done within this unit test in order to properly test the method?

EDIT:

GtlTitle.cs

public class GtlTitle
{
    public string ISBN { get; set; }
    public string VolumeName { get; set; }
    public string TitleDescription { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PublisherName { get; set; }

}

Solution

  • Before going to testing, there are a few things I recommend updating in your code:

    • Make your repository methods and controller actions async (thus web server can process requests while waiting for database roundtrips for previous calls)
    • Use ActionResult as an action return type. This way you can send different http status codes to the client.
    • Return 404 NotFound status code when title not found instead of returning successful result with null as payload.
    • Consider using a RESTful approach for API endpoints. E.g. base uri for titles resource should be something like api/titles
    • Don't specify getTitle for getting title endpoint, because you know HTTP verb which endpoint is mapped to (GET) and base resource url (api/titles).

    With these notes applied:

    [ApiController]
    [Route("api/titles")]
    public class TitleController : Controller
    {
        private IGtlTitleRepository _gtlTitleRepository;
    
        public TitleController(IGtlTitleRepository gtlTitleRepository)
        {
            _gtlTitleRepository = gtlTitleRepository;
        }
    
        [HttpGet("{ISBN}")] // GET api/titles/{ISBN}
        public async Task<ActionResult<GtlTitle>> GetTitle(string ISBN)    
        {
            var title = await _gtlTitleRepository.GetTitle(ISBN);
            if (title == null)
                return NotFound();
    
            return title;
        }
    }
    

    Testing successful title retrieving:

    [Fact]
    public async Task Should_Return_Title_When_Title_Found()
    {
        var repositoryMock = new Mock<IGtlTitleRepository>();
        var title = new GtlTitle();
        repositoryMock.Setup(r => r.Get("978-0-10074-5")).Returns(Task.FromResult(title));
    
        var controller = new TitleController(repositoryMock.Object);
    
        var result = await controller.GetTitle("978-0-10074-5");
        Assert.Equal(title, result.Value);
    }
    

    When title not found:

    [Fact]
    public async Task Should_Return_404_When_Title_Not_Found()
    {
        var repositoryMock = new Mock<IGtlTitleRepository>();
        repositoryMock.Setup(r => r.Get("978-0-10074-5")).Returns(Task.FromResult<GtlTitle>(null));
    
        var controller = new TitleController(repositoryMock.Object);
    
        var result = await controller.GetTitle("978-0-10074-5");
        Assert.IsType<NotFoundResult>(result.Result);
    }