Lasoty
Lasoty

Reputation: 144

How to test Add method with Moq in C#

I try write a unit test for Add method from repository class. I'm using EF6 and Moq. My test method looking that:

public static Mock<DbSet<T>> CreateDbSetMock<T>(IEnumerable<T> elements) where T : class
{
    var elementsAsQueryable = elements.AsQueryable();
    var dbSetMock = new Mock<DbSet<T>>();

    dbSetMock.As<IQueryable<T>>().Setup(m => m.Provider).Returns(elementsAsQueryable.Provider);
    dbSetMock.As<IQueryable<T>>().Setup(m => m.Expression).Returns(elementsAsQueryable.Expression);
    dbSetMock.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(elementsAsQueryable.ElementType);
    dbSetMock.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(elementsAsQueryable.GetEnumerator());

    return dbSetMock;
}

[Test()]
public void AddTest()
{
    // Arrange
    Mock<DbSet<Tytul>> titlesMock = CreateDbSetMock(new List<Tytul>());
    Mock<OzinDbContext> titlesContextMock = new Mock<OzinDbContext>();
    titlesContextMock.Setup(x => x.Tytuly).Returns(titlesMock.Object);
    titlesMock.Setup(x => x.Add(It.IsAny<Tytul>())).Returns((Tytul t) => t);
    IRepository<Tytul> tytulRepository = TytulRepository(titlesContextMock.Object);

    Tytul tytul = new Tytul
    {
        Identyfikator = "ABC"               
    };

    // Act
    tytulRepository.Add(tytul);
         // in Add method:
         //dbContext.Tytuly.Add(entity);
         //dbContext.SaveChanges();
    Tytul tytulInDb = tytulRepository.GetDetail(t => t.Identyfikator == "ABC");
         // in GetDetail method:
         //return dbContext.Tytuly.FirstOrDefault(predicate);

    // Assert
    Assert.AreEqual(tytulInDb.Identyfikator, tytul.Identyfikator);
}

My problem is GetDetail method returns null, but I expected the tytulInDb object. What is wrong? How write thist test correct?

Upvotes: 2

Views: 5622

Answers (1)

Nkosi
Nkosi

Reputation: 247561

Extract the fake data source for the DbSet into a local variable so it can be interacted with later in the test setup. Add a Callback to the Add setup to add the passed argument to the data source so that there is actual data to be acted upon by the mock when invoked.

// Arrange
var data = new List<Tytul>(); //<<< local variable
Mock<DbSet<Tytul>> titlesMock = CreateDbSetMock(data);
var titlesContextMock = new Mock<OzinDbContext>();
titlesContextMock.Setup(x => x.Tytuly).Returns(titlesMock.Object);
titlesMock
    .Setup(x => x.Add(It.IsAny<Tytul>()))
    .Returns((Tytul t) => t)
    .Callback((Tytul t) => data.Add(t)); //<<< for when mocked Add is called.
IRepository<Tytul> tytulRepository = TytulRepository(titlesContextMock.Object);

//...Code removed for brevity

Also when setting up the DbSet Mock use a delegate for the Returns to allow for multiple enumerations as returning just the value will only allow for one pass of the forward only enumerator.

dbSetMock.As<IQueryable<T>>()
    .Setup(m => m.GetEnumerator())
    .Returns(() => elementsAsQueryable.GetEnumerator()); //<<< note delegate

Upvotes: 5

Related Questions