'((System.Linq.IQueryable)((Moq.Mock)(mockDbSet)).Object).Provider'引发了一个类型为'System.NotImplementedException'的异常。

问题描述 投票:1回答:1

我正在尝试模拟多个DbSets和它们的DbContext。 我对这个问题感到很困惑。 我感谢任何反馈。

当我运行测试时,我收到了以下空引用异常。

System.NullReferenceException : Object reference not set to an instance of an object.

在测试方法中,这里抛出了异常。

return enterprisePubContext.WrWaterUsage
    .Include(wu => wu.WaterUseCodeDescription)
    .Include(wu => wu.PhysicalRight)
        .ThenInclude(p => p.WaterRight)
    .FirstOrDefault(wu => wu.PlaceOfUseId == placeOfUseId && wu.PhysicalRight.WaterRight != null);

然而,问题似乎并不在于从测试方法返回结果的查询。 问题似乎发生在设置每个模拟DbSet的过程中,由于对模拟DbSets的设置失败,那么对模拟DbContext的设置随后也失败了,似乎一个空的上下文被传递给了测试方法。 我之前使用moq for DbSets,从来没有遇到过这个问题。

当我调试测试时,我看到每个DbSet和每个 Mock<IQueryable<entity>>.Setup<IQueryProvider>(例如: mockPhysicalRights.As<IQueryable<WrPhysicalRight>>().Setup(m => m.Provider).Returns(physicalRights.Provider)):

'((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure)((Moq.Mock)(mockPhysicalRights)).Object).Instance'抛出一个类型为'System.NotImplementedException'的异常'((System.Linq.IQueryable)((Moq.Mock)('mockDbSet')).Object).Provider'引发了一个类型为'System.NotImplementedException'的异常'((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure)((Moq.Mock)(mockPhysicalRights)).Object)).Instance'引发了一个类型为'System.NotImplementedException'的异常'((System.Linq.IQueryable)((Moq.Mock)()).Instance)。mockDbSet)).Object).Expression'引发了一个类型为'System.NotImplementedException'((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure)((Moq.Mock)(mockPhysicalRights)).Object).Instance'的异常。mockDbSet)).Object).ElementType'引发了一个类型为'System.NotImplementedException'的异常。

下面是调试模式下的异常截图示例。

((Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>)((Moq.Mock)(mockPhysicalRights)).Object).Instance' threw an exception of type 'System.NotImplementedException

'((System.Linq.IQueryable)((Moq.Mock)(mockPhysicalRights)).Object).Provider' threw an exception of type 'System.NotImplementedException'

同样的异常发生在 mockPhysicalRights.As<IQueryable<WrPhysicalRight>>().Setup(m => m.Provider).Returns(physicalRights.Provider) 也被抛出。

mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
    .Setup(m => m.Expression).Returns(physicalRights.Expression);

'((System.Linq.IQueryable)((Moq.Mock)(mockPhysicalRights)).Object).Expression'抛出一个类型为'System.NotImplementedException'的异常。

mockPhysicalRights.As<IQueryable<WrPhysicalRight>>()
    .Setup(m => m.ElementType).Returns(physicalRights.ElementType);

'((System.Linq.IQueryable)((Moq.Mock)(mockPhysicalRights)).Object).ElementType'引发了一个类型为'System.NotImplementedException'的异常。

设置DbContext时。

var mockEnterprisePubContext = new Mock<EnterprisePubContext>();
mockEnterprisePubContext.Setup(m => m.WrPhysicalRight)
    .Returns(mockPhysicalRights.Object);

我看到以下错误。

错误CS0103: 名字'm'在当前上下文中不存在。

下面是调试模式下的截图。

error CS0103: The name 'm' does not exist in the current context

希望我已经包含了下面所有的相关代码。

DbContext--为了简单起见,假设不相关的代码被省略了。

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 - 假设为简单起见,省略了不相关的代码。

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; }
}

测试方法--为简单起见,假设省略了不相关的代码。

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);
        }
    }
}

测试

/// <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);
}
moq dbcontext asp.net-core-3.1 dbset notimplementedexception
1个回答
0
投票

在整理了上面帖子中的思路后,我突然想到,模拟DbSets可能不像创建InMemoryDatabases那样有效。 在尝试模拟测试方法之前,我创建了一个InMemoryDatabase来测试同样的方法,这是创建InMemoryDatabase的代码,成功地用于测试方法。

var options= new DbContextOptionsBuilder<EnterprisePubContext>()
    .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
    .EnableSensitiveDataLogging()
    .Options;
var databaseContext = new EnterprisePubContext(options);
databaseContext.Database.EnsureCreated();

if (!databaseContext.WrPhysicalRight.Any())
{
    databaseContext.WrPhysicalRight.Add(new WrPhysicalRight()
    {
        RightId = 1
    });
}
if (!databaseContext.WrWaterUsage.Any())
{
    databaseContext.WrWaterUsage.Add(new WrWaterUsage()
    {
        RightId = 1,
        WaterUseCode = "01",
        PlaceOfUseId = 1,
        AcreLimit = 12,
        TotalAcres = 12,
        LargePou = false
    });
 }
if (!databaseContext.WrWaterUseCode.Any())
{
    databaseContext.WrWaterUseCode.Add(new WrWaterUseCode()
    {
        WaterUseCode = "01",
        Description = "IRRIGATION"
    });
}
if (!databaseContext.WrWaterRight.Any())
{
    databaseContext.WrWaterRight.Add(new WrWaterRight()
    {
        RightId = 1,
        BasinNumber = 63,
        SequenceNumber = 9874,
        SplitSuffix = "",
        Status = "Active",
        Basis = "Statutory Claim"
    });
}

这个实体初始化对于创建InMemoryDatabase来说,效果不错。 显然,InMemoryDatabase隐式形成了关系。 Moq在模拟DbSets时不会隐式形成关系。 我必须初始化关系属性才能使模拟DbSets工作。

//Mocking physical rights entity
var physicalRights = new List<WrPhysicalRight>
{
    new WrPhysicalRight
    {
        RightId = 1,
        WaterRight = new WrWaterRight
        {
            RightId = 1,
            BasinNumber = 63,
            SequenceNumber = 9874,
            SplitSuffix = "",
            Status = "Active",
            Basis = "Statutory Claim"
        }
    }
}.AsQueryable();

//Mocking water usages entity
var waterUsages = new List<WrWaterUsage>
{
    new WrWaterUsage
    {
        RightId = 1,
        WaterUseCode = "01",
        PlaceOfUseId = 1,
        AcreLimit = 12,
        TotalAcres = 12,
        LargePou = false,
        PhysicalRight = new WrPhysicalRight
        {
            RightId = 1,
            WaterRight = new WrWaterRight
            {
                RightId = 1,
                BasinNumber = 63,
                SequenceNumber = 9874,
                SplitSuffix = "",
                Status = "Active",
                Basis = "Statutory Claim"
            }
        },
        WaterUseCodeDescription = new WrWaterUseCode
        {
            WaterUseCode = "01",
            Description = "IRRIGATION"
        }
    }
}.AsQueryable();

//Mocking water use codes entity
var waterUseCodes = new List<WrWaterUseCode>
{
    new WrWaterUseCode
    {
        WaterUseCode = "01",
        Description = "IRRIGATION",
        Usages = new List<WrWaterUsage>
        {
            new WrWaterUsage
            {
                RightId = 1,
                WaterUseCode = "01",
                PlaceOfUseId = 1,
                AcreLimit = 12,
                TotalAcres = 12,
                LargePou = false,
                PhysicalRight = new WrPhysicalRight
                {
                    RightId = 1,
                    WaterRight = new WrWaterRight
                    {
                        RightId = 1,
                        BasinNumber = 63,
                        SequenceNumber = 9874,
                        SplitSuffix = "",
                        Status = "Active",
                        Basis = "Statutory Claim"
                    }
                },
                WaterUseCodeDescription = new WrWaterUseCode
                {
                    WaterUseCode = "01",
                    Description = "IRRIGATION"
                }
            }
        }
    }
}.AsQueryable();

//Mocking water rights entity
var waterRights = new List<WrWaterRight>
{
    new WrWaterRight
    {
        RightId = 1,
        BasinNumber = 63,
        SequenceNumber = 9874,
        SplitSuffix = "",
        Status = "Active",
        Basis = "Statutory Claim",
        PhysicalRight = new WrPhysicalRight
        {
            RightId = 1,
            WaterRight = new WrWaterRight
            {
                RightId = 1,
                BasinNumber = 63,
                SequenceNumber = 9874,
                SplitSuffix = "",
                Status = "Active",
                Basis = "Statutory Claim"
            }
        }
    }
}.AsQueryable();

显式地将物理权与水权、水使用权与物理权和水使用代码、水使用代码与水使用权、水权与物理权联系起来,这对于正确创建模拟上下文是必要的,该方法用来查询所有这些模拟DbSets。 不知道mocking与InMemoryDatabases在幕后发生了什么区别,但显式创建与mocking的关系是requireed。

© www.soinside.com 2019 - 2024. All rights reserved.