Last active
August 19, 2022 21:48
-
-
Save kelvinrfr/a0a0c1eb65e755f25d68d64dc77defbd to your computer and use it in GitHub Desktop.
Entity Framework unity testing challenges - mocking DbSet / EntityState
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// class `Service` | |
public void Add() | |
{ | |
// this is the code I want to test | |
var entry = await _dbContext.MyDbSetObject.AddAsync(data, cancellationToken); | |
if (entry.State != EntityState.Added) | |
{ | |
// log error & return | |
} | |
await _dbContext.SaveChangesAsync(cancellationToken); | |
} | |
// testing using Moq framework | |
[Fact] | |
public void add_should_not_save_changes_with_invalid_entity_state() | |
{ | |
// Setup | |
var entity = new MyDbSetObject(); | |
var state = EntityState.Unchanged; | |
var stateManager = new Mock<IStateManager>(); | |
stateManager | |
.Setup(manager => manager.CreateEntityFinder(It.IsAny<MyDbSetObject>())) | |
.Returns(Mock.Of<IEntityFinder>()); | |
// known issue on EF6 / NET6 https://github.com/dotnet/efcore/issues/27110 | |
var internalEntityEntry = new InternalEntityEntry( | |
stateManager.Object, | |
new RuntimeEntityType( | |
name: "T", | |
type: typeof(T), | |
sharedClrType: false, | |
model: null, | |
baseType: null, | |
discriminatorProperty: null, | |
ChangeTrackingStrategy.Snapshot, | |
indexerPropertyInfo: null, | |
propertyBag: false), | |
entity); | |
var entry = new Mock<EntityEntry<MyDbSetObject>>(internalEntityEntry); | |
entry.SetupGet(e => e.Entity) | |
.Returns(entity); | |
entry.Setup(entityEntry => entityEntry.State) | |
.Returns(state); | |
var dbSet = new Mock<DbSet<MyDbSetObject>>(); | |
dbSet | |
.Setup(dbSet => dbSet.AddAsync(entity, It.IsAny<CancellationToken>())) | |
.ReturnsAsync(entry.Object); | |
var builder = new DbContextOptionsBuilder<DatabaseContext>(); | |
var dbContext = new Mock<DatabaseContext>(MockBehavior.Loose, builder.Options); | |
dbContext | |
.Setup(context => context.MyDbSetObject) | |
.Returns(dbSet.Object); | |
// ACT | |
var target = new Service(dbContext.Object); | |
target.Add(entity); | |
// Assert | |
dbSet.Verify(moqDbSet => moqDbSet.AddAsync(It.IsAny<MyDbSetObject>(), It.IsAny<CancellationToken>()), | |
Times.Once); | |
dbContext.Verify(context => context.SaveChangesAsync(It.IsAny<CancellationToken>()), | |
Times.Never); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment