-
Notifications
You must be signed in to change notification settings - Fork 26
Description
I wanted to use the DbSet.Remove method in some repository tests, but found that the mock DbSet.Remove method was only doing reference comparison. So passing in an object with identical property values (including the primary key) to an existing item in the collection wouldn't cause it to be removed.
My fix was to update the SetupData extension method signature to add an optional "findEntity" parameter:
public static Mock<DbSet<TEntity>> SetupData<TEntity>(
this Mock<DbSet<TEntity>> mock,
ICollection<TEntity> data = null,
Func<object[], TEntity> find = null,
Func<TEntity, TEntity> findEntity = null
) where TEntity : classAnd then modify the Remove/RemoveRange callbacks to call it:
mock.Setup(m => m.Remove(It.IsAny<TEntity>())).Callback<TEntity>(entity =>
{
if (findEntity != null) entity = findEntity(entity);
data.Remove(entity);
mock.SetupData(data, find, findEntity);
});
mock.Setup(m => m.RemoveRange(It.IsAny<IEnumerable<TEntity>>())).Callback<IEnumerable<TEntity>>(entities =>
{
foreach (var entity in entities)
{
if (findEntity != null) entity = findEntity(entity);
data.Remove(entity);
}
mock.SetupData(data, find, findEntity);
});I then setup my mock to compare primary keys instead of object references:
_mockDbSet.SetupData(_testData,
find: ids => _testData.Find(e => e.EntityId == (int)ids[0]),
findEntity: en => _testData.FirstOrDefault(e => en != null && e.EntityId == en.EntityId)
);Finally, I modified the Add/AddRange callbacks to retain the findEntity parameter:
mock..Setup(m => m.Add(It.IsAny<TEntity>())).Callback<TEntity>(entity =>
{
data.Add(entity);
mock.SetupData(data, find, findEntity);
});
mock.Setup(m => m.AddRange(It.IsAny<IEnumerable<TEntity>>())).Callback<IEnumerable<TEntity>>(entities =>
{
foreach (var entity in entities)
{
data.Add(entity);
};
mock.SetupData(data, find, findEntity);
});Not sure if there's a better way of doing this, but I think it's a helpful enhancement since DbSet.Remove() is often passed a deserialized object from the service layer rather than an object that was constructed by EF, so the expected behaviour is a primary key comparison, not an object reference comparison.