0

I am writing unit tests to test Save DbSet changes. Code looks like:

PreSchoolContext

DbSet<Student> Students{get;set;}

PreSchoolRepository

EDIT: PreSchoolContext is now a IPreSchoolContext instance, handled thru dependency Injection.

public async Task<int> UpdatePreSchoolStudentAsync(Student student)
{   
    PreSchoolContext.Set<Student>().AddOrUpdate(student);
    var result = await PreSchoolContext.SaveChangesAsync().ConfigureAwait(false);
    return result;              
}       

Test Method

//Arrange
var data = GetStudents().AsQueryable();
var mockSet = new Mock<DbSet<Student>>();
mockSet.As<IQueryable<Student>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<Student>(data.Provider));
mockSet.As<IQueryable<Student>>().Setup(m => m.Expression).Returns(data.Expression);

var preSchoolContextMock = new Mock<IPreSchoolContext>();
preSchoolContextMock.Setup(c => c.Students).Returns(mockSet.Object);

var repo = new PreSchoolRepository(preSchoolContextMock.Object);

//Act
var preSchoolStudentUpdateFlag = await repo.UpdatePreSchoolStudentAsync(data.FirstOrDefault());

//Assert
preSchoolStudentUpdateFlag.ShouldNotBeNull();
preSchoolStudentUpdateFlag.ShouldBe(1);

Error

Unable to call public, instance method AddOrUpdate on derived IDbSet type 'Castle.Proxies.DbSet`1Proxy'. Method not found.

Unable to understand what is missing to set the mock data correctly.

Avi Kenjale
  • 2,414
  • 6
  • 29
  • 46
  • Why do we insist on try to test code we have no control over. MS would have tested their code before releasing it. That said, your method under test it too tightly coupled to make testing it in isolation not serve any meaningful purpose. – Nkosi Aug 10 '17 at 16:50
  • 1
    Issue is your class under test is using a real `PreSchoolContext`, by `new`ing it. You need to have some kind of dependency injection in order to mock the dbcontext – AndrewP Aug 11 '17 at 06:36
  • @AndrewP, updated code. Handling thru dependency injection. – Avi Kenjale Aug 25 '17 at 17:39
  • @Nkosi, I understand, however I was able to test DbSet based select /get methods which is under repositories, however unable to test Update methods of repository due to these issues. Due to this, my unit test coverage % less. – Avi Kenjale Aug 25 '17 at 17:43
  • @AviKenjale what problems are you still encountering after the edit – Nkosi Aug 25 '17 at 18:03
  • @Nkosi, I am using method `PreSchoolContext.Set().AddOrUpdate(student);`. I mocked `context, DbSet`, and also setup method `Set()` on context, however, now unit test execution unable to find `AddOrUpdate(params Student[])` somewhere under mocked DbSet. I tried to set it up, however due to signature I cannot. – Avi Kenjale Aug 25 '17 at 18:08
  • @AviKenjale, That method is most likely an extension method. You would need to find the source to know exactly what the method does so that you know what to mock. – Nkosi Aug 25 '17 at 18:09
  • @AviKenjale otherwise you are better or manually copying over the properties that you want to update. – Nkosi Aug 25 '17 at 18:13
  • @AviKenjale That error message confirms that it is an extension method. Moq cannot mock extension methods. – Nkosi Aug 25 '17 at 19:00
  • @Nkosi, sorry, but is there any way to test it? – Avi Kenjale Aug 25 '17 at 19:10

1 Answers1

1

Here I Did Some changes to design to test DbSet<> Update functionality:

Added following function under my context class:

public virtual void AddOrUpdateEntity<TEntity>(IProMetrixContext db, 
TEntity[] entities) where TEntity : class
{
     db.Set<TEntity>().AddOrUpdate(entities);
}   

and then slightly changed UpdatePreSchoolStudentAsync(Student student) method as:

public async Task<int> UpdateProMetrixRiskAsync(Student student)
{
        PreSchoolContext.AddOrUpdateEntity<Student>(PreSchoolContext, student); //This one resolved issue of unit test and works fine in real scenario too.
        var result = await PreSchoolContext.SaveChangesAsync().ConfigureAwait(false);
        return result;
}

Got reference here LINK

NOTE: If anyone has suggestions on same, please post.

Avi Kenjale
  • 2,414
  • 6
  • 29
  • 46