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