5

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; }

}
6
  • 1
    can you show us GtlTitle class? Commented May 25, 2020 at 9:25
  • I edited the original post with it! Commented May 25, 2020 at 9:27
  • 2
    Just check that result from controller equals to title Assert.Equal(title, result);? Commented May 25, 2020 at 9:32
  • 1
    Take a look at this post: exceptionnotfound.net/… ... it should be useful for you Commented May 25, 2020 at 9:34
  • 1
    @PavelAnikhouski Thank you! That line of code worked but...Is that it? I assume that line repositoryMock.Setup(r => r.GetTitle("978-0-10074-5")).Returns(title); basically mocked a database object? Commented May 25, 2020 at 9:40

2 Answers 2

10

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);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much for your detailed response! It is incredibly useful.
I guess, that you should use something like return Ok(title); instead of just return title; in controller
On line await _gtlTitleRepository.GetTitle(ISBN);, it says that `GtlTitle does not contain a definition for GetAwaiter``. Any tips on that?
0

Just to complete this old issue: Regarding the last question from @Questieme: The method has no definition of GetAwaiter, because it is just a void Method and not a task ;).

2 Comments

I don't belleve this is correct, GetTtitle does return something. The issue seems to lie in the return value. Not sure but i believe Ok(title) is expected to work.
I think you mixed the repository with the controller. If he didn't changed the repository it is still not a task: 'public GtlTitle GetTitle(string ISBN)' It's only a task in the controller of the answer from @SergeyBerezovskiy. And also this error message says it pretty clear what the problem is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.