Samo
Samo

Reputation: 23

How to mock database for testing in xUnit?

I have the following 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:

[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.DbContextOptions1[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.IdentityUserContext5..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext8..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext3..ctor(DbContextOptions options) at Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext1..ctor(DbContextOptions options) at Organizer3.Data.OrganizerDbContext..ctor(DbContextOptions1 options) in C:\Users\Smokiel\Desktop\Organizer3\Organizer3\Areas\Identity\Data\OrganizerDbContext.cs:line 15 at Castle.Proxies.OrganizerDbContextProxy..ctor(IInterceptor[] , DbContextOptions`1 options)

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

I tried adding empty constructor by adding public OrganizerDbContext() { } to DbContext, after running test again i got this summary:

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

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.

Upvotes: 1

Views: 572

Answers (1)

Ruikai Feng
Ruikai Feng

Reputation: 11546

as the error indicates

A.Fake<OrganizerDbContext>();

Would not pass the parameters which are required to the constructor of DbContext

you have to pass DbContextOptions<> to the contructor of the dbcontext like below

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

I tried as below,hopes could help:

The Action in 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:

enter image description here

Upvotes: 1

Related Questions