1

I have created ICollectionFixture implementation in hope to create a database instance and share it among various test classes. But, its just not happening and the DBContext gets created on every test method call. On debugging, I can clearly see that after every test method completion , Dispose method of my InMemoryDbContextFixture class gets called , and so every time , a new instance of DBContext gets created which does not saves any of the data that I passed through my first test method..

XUnit document clearly says that when you want to create a single test context and share it among tests in several test classes, we can create ICollectionFixture implementation.

What does sharing mean, when we always are creating a new instance of DBContext? Please help me understand such behavior in name of sharing the instance. I can achieve the present behavior using

Static classes as well. So why to use ICollectionFixture. This interface should prevent me from creating new instance of DBContext for every test case.

My Fixture class goes like

 public class InMemoryDbContextFixture : IDisposable
{
    private static bool _isdbInitialized = false;
    static MyDbContext databaseContext = null;
    private readonly object _lock = new object();
    public InMemoryDbContextFixture()
    {            
        lock (_lock)
        {
            if (!_isdbInitialized)
            {
                var options = new DbContextOptionsBuilder<MyDbContext>()
                           .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
                           .Options;

                databaseContext = new MyDbContext(options);

                databaseContext.Database.EnsureDeleted();       //Ensure first old db is deleted.
                databaseContext.Database.EnsureCreated();

                _isdbInitialized = true;        //db is initialized for the first time.

                // ... initialize data in the test database ...
                Context = databaseContext;
            }
           
        }
    }

    public void Dispose()
    {
        this.Context.Dispose();
    }

    public MyDbContext Context
    {
        get { return databaseContext; }
        private set { }
    }
}

ICollectionFixture implementation.

[CollectionDefinition("SharedTestDatabaseDemo")]
public class SharedTestDatabaseDBContextCollection : ICollectionFixture<InMemoryDbContextFixture>
{
    // This class has no code, and should never be created. Its purpose is simply
    // to be the place to apply [CollectionDefinition] and all the
    // ICollectionFixture<> interfaces.
}

My Test Class

//using namespaces
namespace Project.TestControllers
{
    [Collection("SharedTestDatabaseDemo")]
    public class TestAuthController
    {  
        private readonly MyDbContext myDbContext = null;
       

        public TestAuthController(InMemoryDbContextFixture dbcontextForTest)
        {
            //Initialize non-mocked objects.           
            nimblyDbContext = dbcontextForTest.Context;

            //Arrange mocks 

            objAuthCountroller = new AuthController(mock1.Object, configSettings, myDbContext,
               mock2.Object, mock3.Object);
        }

        [Fact]
        public async Task Test_LoginUserWithCorrectCredentials()
        {
            //Arrange dependencies.
            InputModel logincred = new InputModel() {
                Email = "XunitTestUser@testmail.com",
                Password = "Pass@9799",
                RememberMe = true
            };

            myDbContext.AspNetUserTokens.Add(new AspNetUserTokens
            {
                UserId = "2",
                LoginProvider = "JWT",
                Name = "XunitTestUser@testmail.com"
            });

            myDbContext.AspNetUsers.Add(new AspNetUsers
            {
                Id = "2",
                Name = "XunitTestUser1",
                UserName = "XunitTestUser1",
                Email = "XunitTestUser@testmail.com",
                NormalizedEmail = "XUNITTESTUSER@TESTMAIL.COM",
                EmailConfirmed = true
            });
           
            await myDbContext.SaveChangesAsync();
            

            //ACT
            var result = await objAuthCountroller.Login(logincred);
            Assert.NotNull(result);
        }

        [Fact]
        public async Task Test_ShouldNotAuthenticateUserWithInvalidPassword()
        {
            InputModel logincred = new InputModel();

            logincred.Email = "rahul@testmail.com";
            logincred.Password = "InvalidPassword";
            logincred.RememberMe = true;           
           var result = await objAuthCountroller.Login(logincred);
            Assert.NotNull(result);
        }
    }
}
Rahul Ranjan
  • 1,028
  • 1
  • 10
  • 27
  • 1
    I took your code and it works well. Dispose was called once when testing `TestAuthController` class. Why not use [WebAppFactory](https://learn.microsoft.com/aspnet/core/test/integration-tests?view=aspnetcore-3.1) and configure InMemory db context using `ConfigureTestServices()` in overriden web factory `ConfigureWebHost` method? – Prolog Jul 26 '20 at 07:48
  • @Prolog Yes, I was trying that way....had already written the code for that approach...But then suddenly I thought to run the test project with all test cases...and then I saw ICollectionFixture working as per its documentation..in way that it should work.. So I am planning to freeze this code only as WebAppFactory seems to be more complex code to me...than the above that I have written.. Thanks for the support.. – Rahul Ranjan Jul 26 '20 at 13:02
  • Agree, properly configuring WebApplicationFactory can be a pain. Though, maybe you find [this piece of code](https://gitlab.com/Prologh/parking-lot/-/blob/development/test/IntegrationTests/ParkingLotWebAppFactory.cs) from my privite project helpful. Otherwise, MS documentation and Stack Overflow should be enough to get it working. – Prolog Jul 26 '20 at 14:36

1 Answers1

3

Yes, on running all the test cases at one go, I got the answer..

Actually , we might get this issue when we are trying to test only one Test method and not the entire Test Project. I got confused because when trying to debug Test methods individually in the test class.

Now, when I run all the run cases in visual studio , I can clearly see that ICollectionFixture instance is created only once and shared among various test classes using the collection.

Rahul Ranjan
  • 1,028
  • 1
  • 10
  • 27