Bercovici Adrian
Bercovici Adrian

Reputation: 9360

How to use MOQ in crud operations

I am having the a typical CRUD operation interface (repository) and i was wondering how would someone test it using MOQ.

Model

public class Model
{
   public int Id{get;set;}
}

Interface

public interface ICrud
{
   Task<IEnumerable<Model>> GetAllAsync();
   Task AddAsync(Model model);
}

Service

public class Service
{
   public ICrud operations;

   Service(ICrud crud){ this.operations=crud;}

   public Task<IEnumerable<Model>> GetAllAsync()=>this.operations.GetAllAsync();
   public Task AddAsync(Model model)=> this.operations.AddAsync(model);
}

Unit Test

public class Test
{
   public IEnumerable Seed(){
        yield return new Model {id=3};
        yield return new Model {id =4};
   }
   [Testcase(3)]
   public async Task CanAdd(int id)
   {
         var mock=new Mock<ICrud>();
         var newModel=new Model{ Id=id};
         mock.Setup(x=>x.GetAsync()).ReturnsAsync(Seed);
         mock.Setup(x=>x.AddAsync(newModel));
        //how can i test adding the new model

         var service=new Service(mock.Object);
         var initialList=await service.GetAllAsync();
         //adding
         await service.AddAsync(newModel);
         var finalList=await service.GetAllAsync();

   }
}

My question is , how can i test the following scenario:

-i check the initial collection
-i call `AddAsync`
-i check to see that the new collection contains the added element.

How can this be achieved with Moq in a unit test?

Upvotes: 0

Views: 1518

Answers (2)

Fabio
Fabio

Reputation: 32455

Or you can do it without mocking frameworks.

public class InMemoryCrud : ICrud
{
    public List<Model> Models { get; set; } = new List<Model>();

    public Task<IEnumerable<Model>> GetAllAsync() => return Task.FromResult(Models);        

    public Task AddAsync(Model model)
    {
        Models.Add(model);
        return Task.CompletedTask;
    }
}

public async Task Add_Model() 
{
    var fakeCrud = new InMemoryCrud();
    var service = new Service(fakeCrud);

    var newModel = new Model { Id = 3 };
    await service.AddAsync(newModel);


    var actualModels = await fakeCrud.GetAllAsync();
    var expected = new[]
    {
        new Model { Id = 3 }
    }

    actualModels.Should().BeEquivalentTo(expected); // Pass
}

With InMemoryCrud implementation you can test that correct values ahs been "saved" via crud operations.
With mocking frameworks you will test that correct methods has been called. For example if in Service class I change some properties of the given instance of the Model - tests still pass, but wrong data will be saved to the database in real application.

Upvotes: 1

Nkosi
Nkosi

Reputation: 247153

In this scenario, pass case is that the subject service under test correctly invokes the dependency operation with the given model.

The test should thus reflect that when being exercised.

Using MOQ that would look like

public async Task Service_Should_AddAsync() {
    //Arrange
    int id = 1;
    var mock = new Mock<ICrud>();
    var newModel = new Model { Id = id };
    mock.Setup(x => x.AddAsync(It.IsAny<Model>())).Returns(Task.CompletedTask);

    var service = new Service(mock.Object);

    //Act
    await service.AddAsync(newModel);

    //Assert
    //verify that the mock was invoked with the given model.
    mock.Verify(x => x.AddAsync(newModel));

}

Upvotes: 1

Related Questions