I am attempting to mock multiple DbSets and their DbContext. I'm stumped about this one. I appreciate any feedback.
When I run the test, I receive the following null reference exception:
System.NullReferenceException : Object reference not set to an instance of an object.
The exception is thrown here in the test method:
return enterprisePubContext.WrWaterUsage
.Include(wu => wu.WaterUseCodeDescription)
.Include(wu => wu.PhysicalRight)
.ThenInclude(p => p.WaterRight)
.FirstOrDefault(wu => wu.PlaceOfUseId == placeOfUseId && wu.PhysicalRight.WaterRight != null);
However, it does not seem as if the problem is with the query which returns a result from the test method. The problem appears to occur during setup for each mock DbSet, and since setup for mock DbSets fails, then set up for the mock DbContext subsequently fails, and it seems as if a null context is passed to the test method. I have used moq for DbSets before and never encountered this problem.
When I debug the test, I see the following exception for each DbSet and for each Mock<IQueryable<entity>>.Setup<IQueryProvider>(for example, mockPhysicalRights.As<IQueryable<WrPhysicalRight>>().Setup(m => m.Provider).Returns(physicalRights.Provider)):
'((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure)((Moq.Mock)(mockPhysicalRights)).Object).Instance' threw an exception of type 'System.NotImplementedException' '((System.Linq.IQueryable)((Moq.Mock)('mockDbSet')).Object).Provider' threw an exception of type 'System.NotImplementedException' '((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure)((Moq.Mock)(mockPhysicalRights)).Object).Instance' threw an exception of type 'System.NotImplementedException' '((System.Linq.IQueryable)((Moq.Mock)(mockDbSet)).Object).Expression' threw an exception of type 'System.NotImplementedException' '((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure)((Moq.Mock)(mockPhysicalRights)).Object).Instance' threw an exception of type 'System.NotImplementedException' '((System.Linq.IQueryable)((Moq.Mock)(mockDbSet)).Object).ElementType' threw an exception of type 'System.NotImplementedException'
Here are screenshot examples of the exceptions in debug mode:
The same exception thrown on mockPhysicalRights.As<IQueryable<WrPhysicalRight>>().Setup(m => m.Provider).Returns(physicalRights.Provider) is also thrown on:
mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
.Setup(m => m.Expression).Returns(physicalRights.Expression);
'((System.Linq.IQueryable)((Moq.Mock)(mockPhysicalRights)).Object).Expression' threw an exception of type 'System.NotImplementedException'
mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
.Setup(m => m.ElementType).Returns(physicalRights.ElementType);
'((System.Linq.IQueryable)((Moq.Mock)(mockPhysicalRights)).Object).ElementType' threw an exception of type 'System.NotImplementedException'
When setting up the DbContext,
var mockEnterprisePubContext = new Mock<EnterprisePubContext>();
mockEnterprisePubContext.Setup(m => m.WrPhysicalRight)
.Returns(mockPhysicalRights.Object);
I see the following error:
error CS0103: The name 'm' does not exist in the current context
Here is a screenshot in debug mode.
Hopefully, I have included all the relevant code below.
DbContext - assumed irrelevant code is omitted for simplicity
public class EnterprisePubContext : DbContext
{
public EnterprisePubContext()
{
}
public EnterprisePubContext(DbContextOptions<EnterprisePubContext> options) : base(options)
{
}
public virtual DbSet<WrPhysicalRight> WrPhysicalRight { get; set; }
public virtual DbSet<WrWaterUsage> WrWaterUsage { get; set; }
public virtual DbSet<WrWaterUseCode> WrWaterUseCode { get; set; }
public virtual DbSet<WrWaterRight> WrWaterRight { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<WrPhysicalRight>(entity =>
{
entity.HasKey(e => e.RightId)
.HasName("wrPhysicalRightPK")
.IsClustered(false);
entity.ToTable("wrPhysicalRight", "dbo");
entity.Property(e => e.RightId).HasColumnName("RightID");
entity.HasOne<WrWaterRight>(pr => pr.WaterRight)
.WithOne(wr => wr.PhysicalRight)
.HasForeignKey<WrWaterRight>(wr => wr.RightId);
});
modelBuilder.Entity<WrWaterRight>(entity =>
{
entity.HasKey(e => new { e.BasinNumber, e.SequenceNumber, e.SplitSuffix })
.HasName("wrWaterRightPK")
.IsClustered(false);
entity.ToTable("wrWaterRight", "dbo");
entity.Property(e => e.SplitSuffix)
.HasMaxLength(2)
.IsUnicode(false);
entity.Property(e => e.Basis)
.HasMaxLength(100)
.IsUnicode(false);
entity.Property(e => e.RightId).HasColumnName("RightID");
entity.Property(e => e.Status)
.HasMaxLength(100)
.IsUnicode(false);
});
modelBuilder.Entity<WrWaterUsage>(entity =>
{
entity.HasKey(e => new { e.WaterUseCode, e.RightId })
.HasName("wrWaterUsagePK")
.IsClustered(false);
entity.ToTable("wrWaterUsage", "dbo");
entity.Property(e => e.WaterUseCode)
.HasMaxLength(2)
.IsUnicode(false);
entity.Property(e => e.RightId).HasColumnName("RightID");
entity.Property(e => e.LargePou).HasColumnName("LargePOU");
entity.Property(e => e.PlaceOfUseId).HasColumnName("PlaceOfUseID");
entity.HasOne<WrPhysicalRight>(usage => usage.PhysicalRight)
.WithMany(pr => pr.WaterUsages)
.HasForeignKey(usage => usage.RightId)
.IsRequired();
entity.HasOne<WrWaterUseCode>(usage => usage.WaterUseCodeDescription)
.WithMany(code => code.Usages)
.HasForeignKey(usage => usage.WaterUseCode)
.IsRequired();
});
modelBuilder.Entity<WrWaterUseCode>(entity =>
{
entity.HasKey(e => e.WaterUseCode)
.HasName("wrWaterUseCodePK")
.IsClustered(false);
entity.ToTable("wrWaterUseCode", "dbo");
entity.Property(e => e.WaterUseCode)
.HasMaxLength(2)
.IsUnicode(false)
.ValueGeneratedNever();
entity.Property(e => e.Description)
.HasMaxLength(255)
.IsUnicode(false);
});
}
}
DbSets - assumed irrelevant code is omitted for simplicity
public class WrPhysicalRight
{
[Key]
public int RightId { get; set; }
[ForeignKey("RightId")]
public IList<WrWaterUsage> WaterUsages { get; set; }
public WrWaterRight WaterRight { get; set; }
}
public class WrWaterUsage
{
public string WaterUseCode { get; set; }
public int RightId { get; set; }
public int? PlaceOfUseId { get; set; }
public double? TotalAcres { get; set; }
public double? AcreLimit { get; set; }
public bool LargePou { get; set; }
public WrPhysicalRight PhysicalRight { get; set; }
public WrWaterUseCode WaterUseCodeDescription { get; set; }
}
public class WrWaterUseCode
{
public string WaterUseCode { get; set; }
public string Description { get; set; }
[ForeignKey("WaterUseCode")]
public IList<WrWaterUsage> Usages { get; set; }
}
public class WrWaterRight
{
[Key]
public int BasinNumber { get; set; }
[Key]
public int SequenceNumber { get; set; }
[Key]
public string SplitSuffix { get; set; }
public int? RightId { get; set; }
public string Status { get; set; }
public string Basis { get; set; }
[ForeignKey("RightId")]
public WrPhysicalRight PhysicalRight { get; set; }
}
Test methods - assumed irrelevant code is omitted for simplicity
public abstract class Process : Enumeration<string>
{
protected readonly List<string> Aliases = new List<string>();
private Process(int value, string displayName, IEnumerable<string> aliases) : base(value,
displayName)
{
Aliases.Add(displayName);
Aliases.AddRange(aliases);
}
public static readonly Process WaterRight = new WaterRightProcess();
public static WrWaterUsage WaterUsageByPlaceOfUseId(EnterprisePubContext enterprisePubContext,
int placeOfUseId, ref Process process)
{
if (process != null)
return process.GetWaterUsageByPlaceOfUseId(enterprisePubContext, placeOfUseId);
return null;
}
private class WaterRightProcess : Process
{
internal WaterRightProcess() : base(0, "WaterRight", new [] { "right" })
{
}
protected override WrWaterUsage GetWaterUsageByPlaceOfUseId(EnterprisePubContext
enterprisePubContext, int placeOfUseId)
{
//Exception thrown here
return enterprisePubContext.WrWaterUsage
.Include(wu => wu.WaterUseCodeDescription)
.Include(wu => wu.PhysicalRight)
.ThenInclude(p => p.WaterRight)
.FirstOrDefault(wu => wu.PlaceOfUseId == placeOfUseId && wu.PhysicalRight.WaterRight
!= null);
}
}
}
Test
/// <summary>
/// Ensure that water right process returns PlaceOfUse by PlaceOfUseId appropriately
/// </summary>
[Fact]
public void WaterRightProcessReturnsWaterUsageByPlaceOfUseWithMocking()
{
//Mocking physical rights entity
var physicalRights = new List<WrPhysicalRight>
{
new WrPhysicalRight
{
RightId = 1
}
}.AsQueryable();
var mockPhysicalRights = new Mock<DbSet<WrPhysicalRight>>();
mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
.Setup(m => m.Provider).Returns(physicalRights.Provider);
mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
.Setup(m => m.Expression).Returns(physicalRights.Expression);
mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
.Setup(m => m.ElementType).Returns(physicalRights.ElementType);
mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
.Setup(m => m.GetEnumerator()).Returns(physicalRights.GetEnumerator());
//Mocking water usages entity
var waterUsages = new List<WrWaterUsage>
{
new WrWaterUsage
{
RightId = 1,
WaterUseCode = "01",
PlaceOfUseId = 1,
AcreLimit = 12,
TotalAcres = 12,
LargePou = false
}
}.AsQueryable();
var mockWaterUsages = new Mock<DbSet<WrWaterUsage>>();
mockWaterUsages.As<IQueryable<WrWaterUsage>>()
.Setup(m => m.Provider).Returns(waterUsages.Provider);
mockWaterUsages.As<IQueryable<WrWaterUsage>>()
.Setup(m => m.Expression).Returns(waterUsages.Expression);
mockWaterUsages.As<IQueryable<WrWaterUsage>>()
.Setup(m => m.ElementType).Returns(waterUsages.ElementType);
mockWaterUsages.As<IQueryable<WrWaterUsage>>()
.Setup(m => m.GetEnumerator()).Returns(waterUsages.GetEnumerator());
//Mocking water use codes entity
var waterUseCodes = new List<WrWaterUseCode>
{
new WrWaterUseCode
{
WaterUseCode = "01",
Description = "IRRIGATION"
}
}.AsQueryable();
var mockWaterUseCodes = new Mock<DbSet<WrWaterUseCode>>();
mockWaterUseCodes.As<IQueryable<WrWaterUseCode>>()
.Setup(m => m.Provider).Returns(waterUseCodes.Provider);
mockWaterUseCodes.As<IQueryable<WrWaterUseCode>>()
.Setup(m => m.Expression).Returns(waterUseCodes.Expression);
mockWaterUseCodes.As<IQueryable<WrWaterUseCode>>()
.Setup(m => m.ElementType).Returns(waterUseCodes.ElementType);
mockWaterUseCodes.As<IQueryable<WrWaterUseCode>>()
.Setup(m => m.GetEnumerator()).Returns(waterUseCodes.GetEnumerator());
//Mocking water rights entity
var waterRights = new List<WrWaterRight>
{
new WrWaterRight
{
RightId = 1,
BasinNumber = 63
SequenceNumber = 9874,
SplitSuffix = "",
Status = "Active",
Basis = "Statutory Claim"
}
}.AsQueryable();
var mockWaterRights = new Mock<DbSet<WrWaterRight>>();
mockWaterRights.As<IQueryable<WrWaterRight>>()
.Setup(m => m.Provider).Returns(waterRights.Provider);
mockWaterRights.As<IQueryable<WrWaterRight>>()
.Setup(m => m.Expression).Returns(waterRights.Expression);
mockWaterRights.As<IQueryable<WrWaterRight>>()
.Setup(m => m.ElementType).Returns(waterRights.ElementType);
mockWaterRights.As<IQueryable<WrWaterRight>>()
.Setup(m => m.GetEnumerator()).Returns(waterRights.GetEnumerator());
//Mocking EnterprisePubContext
var mockEnterprisePubContext = new Mock<EnterprisePubContext>();
mockEnterprisePubContext.Setup(m => m.WrPhysicalRight)
.Returns(mockPhysicalRights.Object);
mockEnterprisePubContext.Setup(m => m.WrWaterUsage)
.Returns(mockWaterUsages.Object);
mockEnterprisePubContext.Setup(m => m.WrWaterUseCode)
.Returns(mockWaterUseCodes.Object);
mockEnterprisePubContext.Setup(m => m.WrWaterRight)
.Returns(mockWaterRights.Object);
Process process = Process.WaterRight;
var waterUsage = Process.WaterUsageByPlaceOfUseId(mockEnterprisePubContext.Object, 1, ref
process);
Assert.Equal("01", waterUsage.WaterUseCode);
}



After arranging my thoughts in the above post, it struck me that mocking DbSets might not work like creating InMemoryDatabases. Before trying to mock test the method, I created an InMemoryDatabase to test the same method, and this is the code for creating the InMemoryDatabase which was successfully used to test the method.
This entity initialization worked fine for creating an InMemoryDatabase. Apparently, the InMemoryDatabase implicitly forms relationships. Moq does not implicitly form relationships when mocking DbSets. I had to initialize the relational properties to make the mock DbSets work.
Explicitly relating physical rights with water rights, water usages with physical rights and water use codes, water use codes with water usages, and water rights with physical rights was necessary to properly create the mock context which the method uses to query all those mock DbSets. Not sure about the difference in what is happening behind the scenes with mocking vs. InMemoryDatabases, but explicit creation of relationships with mocking is REQUIRED.