Mocking IQueryable object in TenantDbContext.

huangapple go评论57阅读模式
英文:

Mocking IQueryable object in TenantDbContext

问题

这里是我的DbContext方法:

public class TenantDbContext : IdentityDbContext<AspNetUser, AspNetRole, Guid,
    AspNetUserClaim, AspNetUserRole,
    AspNetUserLogin, AspNetRoleClaim,
    AspNetUserToken>
{
    private readonly IConfiguration _configuration;
    private readonly IHttpContextData _contextData;
    private readonly IDbUtilities _dbUtilities;
    private readonly IMemoryCache _memoryCache;
    private readonly SeedStatikReferanslar _seedStatikReferanslar;
    private readonly SeedRefTip _seedSystemReferanslar;
    private readonly SeedSysParams _seedSysParams;
    private readonly SeedReferanslar _seedReferanslar;

    public TenantDbContext(DbContextOptions<TenantDbContext> options,
        IMemoryCache memoryCache,
        IHttpContextData contextData,
        SeedStatikReferanslar seedStatikReferanslar,
        IConfiguration configuration,
        IDbUtilities dbUtilities,
        SeedRefTip seedSystemReferanslar,
        SeedSysParams seedSysParams,
        SeedReferanslar seedReferanslar,
        bool DoNotSeedDB = false) : base(options)
    {
        _memoryCache = memoryCache;
        _contextData = contextData;
        _seedStatikReferanslar = seedStatikReferanslar;
        _configuration = configuration;
        _dbUtilities = dbUtilities;
        _seedSystemReferanslar = seedSystemReferanslar;
        _seedSysParams = seedSysParams;
        _seedReferanslar = seedReferanslar;
        DoNotSeed = DoNotSeedDB;
    }

    public virtual DbSet<Stok> Stok { get; set; } //(以及更多的DbSet)
}

这是我的存储库示例:

public class StokRepository : AsyncRepository<Stok>, IStokRepository
{
    private readonly TenantDbContext _tenantDbContext;
    public StokRepository(TenantDbContext dbContext) : base(dbContext)
    {
        _tenantDbContext = dbContext;
    }

    public async Task<Stok> GetByIdAsync(Guid Id, bool withRelationalTables = false, bool withReferences = false)
    {
        var query = _tenantDbContext.Stok.Where(x => x.Id == Id);//我想设置这个查询

        if (withRelationalTables)
        {
            query = query
                .Include(x => x.StokNotlar)
                .Include(x => x.StokTeminler).ThenInclude(x => x.CariHesap)
                .Include(x => x.StokFiyat)
                .Include(x => x.StokMuhasebe)
                .Include(x => x.StokBirimler).ThenInclude(x => x.BirimBarkods)
                .Include(x => x.GTIP);
        }

        Stok stok = await query.FirstOrDefaultAsync();
        if (stok == null)
            return null;

        if (withReferences)
            await MatchStokWithReferans(new List<Stok> { stok });

        stok = (await GetAllStokWithSanalSahalarAsync(new List<Stok> { stok })).FirstOrDefault();

        return stok;
    }
}

我想设置:

var query = _tenantDbContext.Stok.Where(x => x.Id == Id);

我的测试方法:

[Fact]
public async void StokRepoTest()
{
    IQueryable<Stok> data = new List<Stok>()
    {
      new Stok()
      {
       Id = Guid.NewGuid(),
       MalKodu  = "abc"
    }}.AsQueryable();

    var mockSet = new Mock<DbSet<Stok>>();
    mockSet.As<IQueryable<Stok>>().Setup(m => m.Provider).Returns(data.Provider);
    mockSet.As<IQueryable<Stok>>().Setup(m => m.Expression).Returns(data.Expression);
    mockSet.As<IQueryable<Stok>>().Setup(m => m.ElementType).Returns(data.ElementType);
    mockSet.As<IQueryable<Stok>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

    var contextMock = new Mock<TenantDbContext>
    (
     new DbContextOptions<TenantDbContext>(),
     null,
     null,
     null,
     null,
     null,
     null,
     null,
     null,
     false);
    contextMock.Setup(x => x.Stok)
               .Returns(mockSet.Object);

    StokRepository stokRepository = new StokRepository(contextMock.Object);

    var stok = await stokRepository.GetByIdAsync(Guid.NewGuid(), false, false);

    Assert.NotNull(stok);
}

在执行时返回的错误消息:

The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations
英文:

Here is my DbContext methods:

public class TenantDbContext : IdentityDbContext<AspNetUser, AspNetRole, Guid,
    AspNetUserClaim, AspNetUserRole,
    AspNetUserLogin, AspNetRoleClaim,
    AspNetUserToken>
{
    private readonly IConfiguration _configuration;
    private readonly IHttpContextData _contextData;
    private readonly IDbUtilities _dbUtilities;
    private readonly IMemoryCache _memoryCache;
    private readonly SeedStatikReferanslar _seedStatikReferanslar;
    private readonly SeedRefTip _seedSystemReferanslar;
    private readonly SeedSysParams _seedSysParams;
    private readonly SeedReferanslar _seedReferanslar;

    public TenantDbContext(DbContextOptions<TenantDbContext> options,
        IMemoryCache memoryCache,
        IHttpContextData contextData,
        SeedStatikReferanslar seedStatikReferanslar,
        IConfiguration configuration,
        IDbUtilities dbUtilities,
        SeedRefTip seedSystemReferanslar,
        SeedSysParams seedSysParams,
        SeedReferanslar seedReferanslar,
        bool DoNotSeedDB  = false) : base(options)
    {
        _memoryCache = memoryCache;
        _contextData = contextData;
        _seedStatikReferanslar = seedStatikReferanslar;
        _configuration = configuration;
        _dbUtilities = dbUtilities;
        _seedSystemReferanslar = seedSystemReferanslar;
        _seedSysParams = seedSysParams;
        _seedReferanslar = seedReferanslar;
        DoNotSeed = DoNotSeedDB;
    } 
      {
       public virtual DbSet<Stok> Stok { get; set; } //(and more DbSet)
      }
    }

Here are my Repository samples:

public class StokRepository : AsyncRepository<Stok>, IStokRepository
{
    private readonly TenantDbContext _tenantDbContext;
    public StokRepository(TenantDbContext dbContext) : base(dbContext)
    {
        _tenantDbContext = dbContext;
    }

    public async Task<Stok> GetByIdAsync(Guid Id, bool withRelationalTables = false, bool withReferences = false)
    {
        var query = _tenantDbContext.Stok.Where(x => x.Id == Id);//I want setup this 

        if (withRelationalTables)
        {
            query = query
                .Include(x => x.StokNotlar)
                .Include(x => x.StokTeminler).ThenInclude(x => x.CariHesap)
                .Include(x => x.StokFiyat)
                .Include(x => x.StokMuhasebe)
                .Include(x => x.StokBirimler).ThenInclude(x => x.BirimBarkods)
                .Include(x => x.GTIP);
        }

        Stok stok = await query.FirstOrDefaultAsync();
        if (stok == null)
            return null;

        if (withReferences)
            await MatchStokWithReferans(new List<Stok> { stok });

        stok = (await GetAllStokWithSanalSahalarAsync(new List<Stok> { stok })).FirstOrDefault();

        return stok;
}

i want to setup :

 var query = _tenantDbContext.Stok.Where(x => x.Id == Id);

my test method:

        [Fact]
        public async void StokRepoTest()
        {
            IQueryable<Stok> data = new List<Stok>()
            {
              new Stok()
              {
               Id = Guid.NewGuid(),
               MalKodu  = "abc"
            }}.AsQueryable();

            var mockSet = new Mock<DbSet<Stok>>();
            mockSet.As<IQueryable<Stok>>().Setup(m => m.Provider).Returns(data.Provider);
            mockSet.As<IQueryable<Stok>>().Setup(m => m.Expression).Returns(data.Expression);
            mockSet.As<IQueryable<Stok>>().Setup(m => m.ElementType).Returns(data.ElementType);
            mockSet.As<IQueryable<Stok>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

            var contextMock = new Mock<TenantDbContext>
            (
             new DbContextOptions<TenantDbContext>(),
             null,
             null,
             null,
             null,
             null,
             null,
             null,
             null,
             false);
            contextMock.Setup(x => x.Stok)
                       .Returns(mockSet.Object);

            StokRepository stokRepository = new StokRepository(contextMock.Object);

            var stok = await stokRepository.GetByIdAsync(Guid.NewGuid(), false, false);

            Assert.NotNull(stok);
        }

The error message that is returned while :

The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations

答案1

得分: 1

In order to override members of your class, you need to use the virtual keyword. So try to change to:

public virtual DbSet<Stok> Stok { get; set; }
英文:

In order to override members of your class, you need to use the virtual keyword. So try to change to:

public virtual DbSet&lt;Stok&gt; Stok { get; set; }

答案2

得分: 1

Instead of trying to mock the context, use an in-memory database:

var options = new DbContextOptionsBuilder().UseInMemoryDatabase("YourInMemoryDbName").Options;

You can then use this options object to create your context instance.

Populate your in-memory database with your sample records prior to running the test.

You can read more about it here.

It should be pointed out that this kind of testing is discouraged, mainly because of the differences between the implementations of the In-Memory database and a real MS SQL instance.

You already have a repository abstraction on top of the context so mocking the repos should suffice for your testing purposes. If you find yourself needing to unit test your repos, then it's time to take a step back and review your code as repos should be pretty dumb and only implement logic needed to access the data store.

英文:

Instead of trying to mock the context, use an in-memory database:

var options = new DbContextOptionsBuilder().UseInMemoryDatabase(&quot;YourInMemoryDbName&quot;).Options;      

You can then use this options object to create your context instance.

Populate your in-memory database with your sample records prior to running the test.

You can read more about it here.

It should be pointed out that this kind of testing is discouraged, mainly because of the differences between the implementations of the In-Memory database and a real MS SQL instance.

You already have a repository abstraction on top of the context so mocking the repos should suffice for your testing purposes. If you find yourself needing to unit test your repos, then it's time to take a step back and review your code as repos should be pretty dumb and only implement logic needed to access the data store.

huangapple
  • 本文由 发表于 2023年5月22日 21:19:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76306621.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定