yogihosting
yogihosting

Reputation: 6322

How to test web api method with xUnit and Moq when there is "this" keyword

I am trying to test the web api method with xUnit and Moq but there comes a situation as it is using "this" keyword. Please see the code.

Web API Inteface

public interface IRepository
{
    IEnumerable<Reservation> Reservations { get; }
    Reservation this[int id] { get; }
}

There is this keyword Reservation this[int id] { get; }

The Web API Controller

[ApiController]
[Route("api/[controller]")]
public class ReservationController : ControllerBase
{
    private IRepository repository;
    public ReservationController(IRepository repo) => repository = repo;

    [HttpGet]
    public IEnumerable<Reservation> Get() => repository.Reservations;

    [HttpGet("{id}")]
    public ActionResult<Reservation> Get(int id)
    {
        if (id == 0)
            return BadRequest("Value must be passed in the request body.");

        Reservation r = repository[id];

        if (r is null)
            return NotFound();

        return Ok(r);
    }
}

The web api provides records from in-memory and not from database. There is a class Repository.cs which contains records hard-coded.

public class Repository : IRepository
{
    private Dictionary<int, Reservation> items;

    public Repository()
    {
        items = new Dictionary<int, Reservation>();
        new List<Reservation> {
            new Reservation {Id=1, Name = "Jack", StartLocation = "Edinberg", EndLocation="Yeti" },
            new Reservation {Id=2, Name = "Nick", StartLocation = "Moscow", EndLocation="LKO" },
            new Reservation {Id=3, Name = "Ramon", StartLocation = "London", EndLocation="Paris" }
            }.ForEach(r => AddReservation(r));
    }

    public Reservation this[int id] => items.ContainsKey(id) ? items[id] : null;

    public IEnumerable<Reservation> Reservations => items.Values;

 }

Now I want to mock for the web api method Get(int id) but can't because of 'this' keyword. I tried below code but failed.

[Fact]
public void Test_GET_AReservations_BadRequest()
{
    // Arrange
    var mockRepo = new Mock<IRepository>();
    mockRepo.Setup(repo => repo.Reservations.this).Returns(Single());
    var controller = new ReservationController(mockRepo.Object);

    // Act
    var result = controller.Get();

    // Assert
    var model = Assert.IsAssignableFrom<IEnumerable<Reservation>>(result);
    Assert.Equal(3, model.Count());
}

private static Reservation Single()
{
    return new Reservation()
    {
        Id = 1,
        Name = "Test One",
        StartLocation = "SL1",
        EndLocation = "EL1"
    };
}

The mock code to look for is mockRepo.Setup(repo => repo.Reservations.this).Returns(Single());. I get compile error. So please help me to fix it.

Upvotes: 1

Views: 93

Answers (2)

Nkosi
Nkosi

Reputation: 247323

In order to recreate the desired behavior you can capture the passed argument in the Returns and use it to create the fake model

[Fact]
public void Should_GET_AReservation_By_Id() {
    // Arrange
    var mockRepo = new Mock<IRepository>();
    mockRepo.Setup(repo => repo[It.IsAny<int>()])
        .Returns((int id) => Single(id));

    var controller = new ReservationController(mockRepo.Object);
    int id = 123456

    // Act
    ActionResult<Reservation> result = controller.Get(id);
    Reservation actual = result.Value;

    // Assert
    actual.Should().NotBeNull();
    actual.Id.Should().Be(id);
}

private static Reservation Single(int id) =>
    return new Reservation() {
        Id = id,
        Name = "Test One",
        StartLocation = "SL1",
        EndLocation = "EL1"
    };

Note the use of It.IsAny<int> argument matcher to accept any integer passed into the indexer.

Upvotes: 2

Shafiq Jetha
Shafiq Jetha

Reputation: 1564

This might work for you:

mockRepo
    .SetupSet(repo => repo[123456] = It.IsAny<int>())
    .Callback((string name, object m) => {Single()});

Source: https://weblogs.asp.net/bleroy/mocking-indexer-setters-with-moq

Upvotes: 0

Related Questions