简体   繁体   English

如何在 xUnit 中模拟数据库进行测试?

[英]How to mock database for testing in xUnit?

I have the following DbContext :我有以下DbContext

public class OrganizerDbContext : IdentityDbContext<AppUser>
{
    public OrganizerDbContext(DbContextOptions<OrganizerDbContext> options)
        : base(options)
    {
    }
 
    // list of dbSet<> here

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<AppUser>()
            .HasOne(a => a.Accesses)
            .WithOne(a => a.User)
            .HasForeignKey<UserAccess>(a => a.UserId);

        builder.Entity<AppUser>()
            .HasOne(a=>a.EmploymentStatus)
            .WithOne(a=>a.User)
            .HasForeignKey<EmploymentStatus>(a => a.UserId);

        builder.Entity<EmploymentStatus>()
            .HasOne(a=>a.Facility)
            .WithMany(a=>a.Employments)
            .HasForeignKey(a => a.FacilityId);        
    }
}

In my xUnit test, I used FakeItEasy and have following code:在我的 xUnit 测试中,我使用FakeItEasy并具有以下代码:

[Fact]
public async Task AnnouncerIndex_Test_1()
{
    // Arrange
    var fakeContext = A.Fake<OrganizerDbContext>();
    var fakeUserManager = A.Fake<UserManager<AppUser>>();
    var controller = new AnnouncementsController(fakeContext, fakeUserManager);

    // Act
    var result = await controller.IsUserBlockedFromAccesingAnnouncer();

    // Assert
    Assert.False(result);
}

The test fails and I get this in test details summary:测试失败,我在测试详细信息摘要中得到了这个:

Message: FakeItEasy.Core.FakeCreationException: Failed to create fake of type Organizer3.Data.OrganizerDbContext: Below is a list of reasons for failure per attempted constructor: Constructor with signature (Microsoft.EntityFrameworkCore.DbContextOptions 1[Organizer3.Data.OrganizerDbContext]) failed: No constructor matches the passed arguments for constructor. An exception of type System.InvalidOperationException was caught during this call. Its message was: The DbContextOptions passed to the OrganizerDbContextProxy constructor must be a DbContextOptions<OrganizerDbContextProxy>. When registering multiple DbContext types, make sure that the constructor for each context type has a DbContextOptions<TContext> parameter rather than a non-generic DbContextOptions parameter. at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext消息:FakeItEasy.Core.FakeCreationException:无法创建 Organizer3.Data.OrganizerDbContext 类型的伪造:以下是每个尝试的构造函数失败的原因列表:具有签名的构造函数 (Microsoft.EntityFrameworkCore.DbContextOptions 1[Organizer3.Data.OrganizerDbContext]) failed: No constructor matches the passed arguments for constructor. An exception of type System.InvalidOperationException was caught during this call. Its message was: The DbContextOptions passed to the OrganizerDbContextProxy constructor must be a DbContextOptions<OrganizerDbContextProxy>. When registering multiple DbContext types, make sure that the constructor for each context type has a DbContextOptions<TContext> parameter rather than a non-generic DbContextOptions parameter. at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext 1[Organizer3.Data.OrganizerDbContext]) failed: No constructor matches the passed arguments for constructor. An exception of type System.InvalidOperationException was caught during this call. Its message was: The DbContextOptions passed to the OrganizerDbContextProxy constructor must be a DbContextOptions<OrganizerDbContextProxy>. When registering multiple DbContext types, make sure that the constructor for each context type has a DbContextOptions<TContext> parameter rather than a non-generic DbContextOptions parameter. at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext 1[Organizer3.Data.OrganizerDbContext]) failed: No constructor matches the passed arguments for constructor. An exception of type System.InvalidOperationException was caught during this call. Its message was: The DbContextOptions passed to the OrganizerDbContextProxy constructor must be a DbContextOptions<OrganizerDbContextProxy>. When registering multiple DbContext types, make sure that the constructor for each context type has a DbContextOptions<TContext> parameter rather than a non-generic DbContextOptions parameter. at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext 5..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext 8..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext 3..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext 1..ctor(DbContextOptions options) at Organizer3.Data.OrganizerDbContext..ctor(DbContextOptions 1 options) in C:\Users\Smokiel\Desktop\Organizer3\Organizer3\Areas\Identity\Data\OrganizerDbContext.cs:line 15 at Castle.Proxies.OrganizerDbContextProxy..ctor(IInterceptor[], DbContextOptions`1 options) 1[Organizer3.Data.OrganizerDbContext]) failed: No constructor matches the passed arguments for constructor. An exception of type System.InvalidOperationException was caught during this call. Its message was: The DbContextOptions passed to the OrganizerDbContextProxy constructor must be a DbContextOptions<OrganizerDbContextProxy>. When registering multiple DbContext types, make sure that the constructor for each context type has a DbContextOptions<TContext> parameter rather than a non-generic DbContextOptions parameter. at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext 5..ctor(DbContextOptions 选项)在 Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext 8..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext 3..ctor(DbContextOptions 选项)在 Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext 1..ctor(DbContextOptions options) at Organizer3.Data.OrganizerDbContext..ctor(DbContextOptions 1 选项)中C:\Users\Smokiel\Desktop\Organizer3\Organizer3\Areas\Identity\Data\OrganizerDbContext.cs: 第 15 行在 Castle.Proxies.OrganizerDbContextProxy..ctor(IInterceptor[], DbContextOptions`1 选项)

Stack Trace: FailedCreationResult.get_Result() line 82 FakeAndDummyManager.CreateFake(Type typeOfFake, Action`1 optionsBuilder, LoopDetectingResolutionContext resolutionContext) line 42 FakeAndDummyManager.CreateFake(Type typeOfFake, LoopDetectingResolutionContext resolutionContext) line 28 A.FakeT line 31 AnnouncementsControllerTests.AnnouncerIndex_Test_1() line 28 --- End of stack trace from previous location ---堆栈跟踪:FailedCreationResult.get_Result() 第 82 行 FakeAndDummyManager.CreateFake(类型 typeOfFake,Action`1 optionsBuilder,LoopDetectingResolutionContext resolutionContext)第 42 行 FakeAndDummyManager.CreateFake(类型 typeOfFake,LoopDetectingResolutionContext resolutionContext)第 28 行 A.FakeT 第 31 行 AnnouncementsControllerTests.AnnouncerIndex_Test_1() 28 --- 上一个位置的堆栈跟踪结束 ---

I tried adding empty constructor by adding public OrganizerDbContext() { } to DbContext, after running test again i got this summary:我尝试通过将public OrganizerDbContext() { }添加到 DbContext 来添加空构造函数,再次运行测试后我得到了这个摘要:

Message: System.ArgumentException: Object 'Organizer3.Controllers.AnnouncementsController' of type Organizer3.Controllers.AnnouncementsController is not recognized as a fake object. Stack Trace: DefaultFakeManagerAccessor.GetFakeManager(Object proxy) line 28 Fake.GetFakeManager(Object fakedObject) line 28 FakeConfigurationManager.GuardAgainstNonFake(Object target) line 83 FakeConfigurationManager.CallTo[T](Expression`1 callSpecification) line 45 A.CallTo[T](Expression`1 callSpecification) line 161 AnnouncementsControllerTests.AnnouncerIndex_Test_1() line 34 --- End of stack trace from previous location ---消息:System.ArgumentException:Object Organizer3.Controllers.AnnouncementsController 类型的“Organizer3.Controllers.AnnouncementsController”未被识别为伪造的 object。堆栈跟踪:DefaultFakeManagerAccessor.GetFakeManager(对象代理)第 28 行 Fake.GetFakeManager(对象伪造的对象)第 28 行FakeConfigurationManager.GuardAgainstNonFake(Object target) 第 83 行 FakeConfigurationManager.CallTo[T](Expression`1 callSpecification) 第 45 行 A.CallTo[T](Expression`1 callSpecification) 第 161 行 AnnouncementsControllerTests.AnnouncerIndex_Test_1() 第 34 行 --- 堆栈结束从以前的位置跟踪 ---

I expected to get a dummy context I could use for testing, it wasn't called anywhere outside of controller constructor, and I can't get it running.我希望得到一个可以用于测试的虚拟上下文,它没有在 controller 构造函数之外的任何地方调用,而且我无法运行它。

as the error indicates如错误所示

A.Fake<OrganizerDbContext>();

Would not pass the parameters which are required to the constructor of DbContext不会将所需的参数传递给 DbContext 的构造函数

you have to pass DbContextOptions<> to the contructor of the dbcontext like below您必须将 DbContextOptions<> 传递给 dbcontext 的构造函数,如下所示

A.Fake<WebApplication6Context>(x => x.WithArgumentsForConstructor(new object[] { options }));

I tried as below,hopes could help:我试过如下,希望能有所帮助:

The Action in controller: controller 中的操作:

 public List<SomeEntity> GetEntities()
        {
              return  _context.SomeEntity.ToList();
        }

The Dbcontext:数据库上下文:

public class WebApplication6Context : DbContext
    {
        public WebApplication6Context (DbContextOptions<WebApplication6Context> options)
            : base(options)
        {
        }

        public virtual DbSet<WebApplication6.Models.SomeEntity>? SomeEntity { get; set; } 

        
    }

Codes related with Test测试相关代码

[Fact]
        public void Test1()
        {

            

            var data = new List<SomeEntity>() { new SomeEntity() { Id = 1, Name = "N1" }, new SomeEntity() { Id = 2, Name = "N2" } }.AsQueryable();

            var fakeDbSet = A.Fake<DbSet<SomeEntity>>(x=> 
            { 
               
                x.Implements(typeof(IQueryable<SomeEntity>));
                x.Implements(typeof(IEnumerable<SomeEntity>));
               
            });
            
            A.CallTo(() => ((IQueryable<SomeEntity>)fakeDbSet).GetEnumerator()).Returns(data.GetEnumerator());

            
            var fakecontext = A.Fake<WebApplication6Context>(x =>
            {
                x.WithArgumentsForConstructor(new object[] {  new DbContextOptionsBuilder<WebApplication6Context>().Options });

            });
            
            A.CallTo(() => fakecontext.SomeEntity).Returns(fakeDbSet);
            var controller = new SomeEntitiesController(fakecontext);
            //Act
            var result=controller.GetEntities();
            //Assert
            Assert.Equal(2,result.Count);

        }

The Result:结果:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM