C# Tests failing using XUnit and EF InMemoryDatabase - c#

I'm adding integration tests to a project using XUnit and EF core InMemoryDatabase.
Currently, there are 2 Test classes: RolesControllerTest and UsersControllerTest.
For some reason, when I run tests one by one, they pass, but if I run them all at once, some of them fail.
If I mark the two classes with
[Collection("Our Test Collection #1")]
According to XUnit documentation: "If we need to indicate that multiple test classes should not be run in parallel against one another, then we place them into the same test collection. This is simply a matter of decorating each test class with an attribute that places them into the same uniquely named test collection". https://xunit.net/docs/running-tests-in-parallel
All the tests pass in both Test Classes if they are part of the same Collection (not running in parallel).
This is the Fixture that uses the WebApplicationFactory:
public class TestFactory<TProgram, TDbContext> : WebApplicationFactory<TProgram>
where TProgram : class where TDbContext : DbContext
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.RemoveDbContext<TDbContext>();
services.AddDbContext<TDbContext>(options =>
{
options.UseInMemoryDatabase("TestDB");
});
services.EnsureDbCreated<TDbContext>();
});
}
}
This is the TestFactoryBase class (Uses the previous TestFactory Fixture):
public class TestFactoryBase : IClassFixture<TestFactory<Program, ADBContext>>, IDisposable
{
public HttpClient _client;
public ADBContext _dbContext;
public TestFactoryBase(TestFactory<Program, ADBContext> factory) {
_client = factory.CreateClient();
var scope = factory.Services.CreateScope();
var scopedServices = scope.ServiceProvider;
_dbContext = scopedServices.GetService<ADBContext>();
_dbContext.Database.EnsureCreated();
}
public void Dispose()
{
_client.Dispose();
_dbContext.Database.EnsureDeleted();
}
}
This is the Role test class:
public class RolesControllerTest : TestFactoryBase, IClassFixture<RolesRepositoryFixture>
{
private IRoleRepository _rolesRepository;
public RolesControllerTest(TestFactory<Program, ADBContext> factory, RolesRepositoryFixture rolesRepository) : base(factory)
{
_rolesRepository = rolesRepository.CreateRoleRepository(_dbContext);
}
// Tests...
This is a test:
[Fact(DisplayName = "GetByIdAsync returns a role model by Id")]
public async Task GetByIdAsync_ReturnsRoleModel()
{
var roleInDb = await _rolesRepository.CreateAsync(new RoleModel
{
Id = Guid.NewGuid(),
Name = "Role A",
Description = "Role A Description",
CreatedDate = DateTime.Now,
UpdatedDate = DateTime.Now,
Deleted = false,
});
var response = await _client.GetFromJsonAsync<RoleModel>($"/api/roles/{roleInDb.Id}");
response.ShouldNotBeNull();
response.ShouldBeOfType<RoleModel>();
response.Id.ShouldBe(roleInDb.Id);
response.Name.ShouldBe(roleInDb.Name);
response.Description.ShouldBe(roleInDb.Description);
}
I've been trying to debug the tests, but haven't found the exact problem yet.
How can I fix it without having to mark all Test Classes as part of the same collection?

Related

Why do other tests in a collection run when I select a single test to run?

I have a class containing four tests, this class is part of a collection with a shared database fixture. When I execute a single test in the class from Test Explorer in Visual Studio it passes as expected. However, looking at the state of the database after it runs, it seems that some of the other tests are also attempting to run. SQL profiler confirmed this.
SQL profiler also revealed that the database fixture constructor is being executed multiple times when a single test is selected to run.
The behavior is the same when I execute just the one test by right clicking it in the code editor and selecting run.
I tried removing the class from the collection, and instantiating the database fixture within each of the test methods in the class.
This actually does result in the expected database state after a single test has been run. I just don't understand why removing the tests from the collection results in different behavior. From my understanding, the whole point of a collection fixture is to have it run once for all tests in the collection.
It would be preferable to keep the tests in a single collection, as the database fixture loads some data in it's constructor. It is much slower to instantiate it for every test and requires a fair amount of wiring in each test method.
Simplified example:
public class DatabaseFixture : IDisposable
{
internal IDbConnection Db;
internal IConfiguration Configuration;
internal OrmLiteConnectionFactory dbFactory;
public DatabaseFixture()
{
Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var connectionString = Configuration.GetConnectionString(name: "Connection");
dbFactory
= new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider, false);
Db = dbFactory.Open();
Db.ExecuteNonQuery("DELETE FROM TestTable");
}
public void Dispose()
{
}
}
[CollectionDefinition("TestCollection")]
public class TestCollection : ICollectionFixture<DatabaseFixture>
{
}
[Collection("TestCollection")]
public class TestClass
{
DatabaseFixture dbFixture;
public TestClass(DatabaseFixture database)
{
dbFixture = database;
}
[Fact]
public void Test1()
{
dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 1" });
}
[Fact]
public void Test2()
{
dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 2" });
}
}
Executing only Test1 produces the following in the table:
Then when I change TestClass to the following:
public class TestClass
{
DatabaseFixture dbFixture;
[Fact]
public void Test1()
{
dbFixture = new();
dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 1" });
}
[Fact]
public void Test2()
{
dbFixture = new();
dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 2" });
}
}
And execute only Test1, I get the expected result:

How can I get the results from mock a endpoint with a local json file?

I'm pretty new in .NET and I have to mock GetCustomerDetailsAsync endpoint using a local json file in order to get the file results and create a way to enable and disable the mocking mechanism. Here is the handler where I have to implement the logic
public class GetCustomerDetailsQueryHandler : IRequestHandler<GetCustomerDetailsQuery, CustomerInsertionModel>
{
private readonly ICustomerApiClient _customerApiClient;
private readonly IAccountApiClientGraphQl _accountApiClientGraphQl;
private readonly IMapper _mapper;
private readonly IMediator _mediator;
private readonly IOptions<CommonFeaturesOptions> _commonFeaturesOptions;
public GetCustomerDetailsQueryHandler(ICustomerApiClient customerApiClient,
IAccountApiClientGraphQl accountApiClientGraphQl,
IMediator mediator,
IMapper mapper,
IOptions<CommonFeaturesOptions> commonFeaturesOptions)
{
_customerApiClient = customerApiClient ?? throw new ArgumentNullException(nameof(customerApiClient));
_accountApiClientGraphQl = accountApiClientGraphQl ?? throw new ArgumentNullException(nameof(accountApiClientGraphQl));
_mediator = mediator;
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_commonFeaturesOptions = commonFeaturesOptions ?? throw new ArgumentNullException(nameof(commonFeaturesOptions));
}
public async Task<CustomerInsertionModel> Handle(GetCustomerDetailsQuery request, CancellationToken cancellationToken)
{
//if(!EnablePostfixations){
//var customerApiResponse = (await _customerApiClient.GetCustomerDetailsAsync(_mapper.Map<GetCustomerDetailsRequest>(request)))
// .CustomerDetails.FirstOrDefault();
//}
//else
//{
//var response = await _customerApiClient.GetCustomerDetailsAsync(
// _mapper.Map<GetCustomerDetailsRequest>((request, _commonFeaturesOptions.Value)));
var customerApiResponse = (await _customerApiClient.GetCustomerDetailsAsync(_mapper.Map<GetCustomerDetailsRequest>(request)))
.CustomerDetails.FirstOrDefault();
if (customerApiResponse is null)
{
return new CustomerInsertionModel((false, string.Empty))
.MarkCustomerAsNotFound();
}
var customer = new CustomerInsertionModel(customerApiResponse.Cif,
customerApiResponse.CnpCui,
customerApiResponse.Category,
customerApiResponse.Insolvency,
request.AddInsolvency,
request.CloseCustomerCategories.ToList());
return customer
}
}
commented lines are just for guidence (what to do, inject an IOptions with a boolean that decide when to enable and disable the mocking mechanism )
plus I wanna know where should I have to deserialize the file(in handler or outside of it?)
public class DeserializeFromFileAsync
{
public class PostfixationsFile
{
public string Customer_no { get; set; }
public string First_name { get; set; }
public string Last_name { get; set; }
public string Customer_type { get; set; }
public string Adress_line { get; set; }
public int Cnp { get; set; }
public string Customer_category { get; set; }
public int Open_branch { get; set; }
public string Branch_name { get; set; }
public string Insolventa { get; set; }
}
public class Program
{
public static async Task Main()
{
string fileName = #"FullPath";
using FileStream openStream = File.OpenRead(fileName);
PostfixationsFile weatherForecast = await JsonSerializer.DeserializeAsync<PostfixationsFile>(openStream);
}
}
}
and this is the json file.
[
{ "customer_no": "03242524",
"first_name": "Prenume",
"last_name": "Nume",
"customer_type": "PF",'
"adress_line": " Str. FN Nr. Bl. Sc. Et. Ap. Sect. Loc. Jud. ",
"cnp": "1970907336523",
"customer_category": "PF_ONLINE",
"open_branch": "213",
"branch_name": "SUCURSALA DEJ",
"insolventa": "NU"
},
{ "customer_no": "03242524",
"first_name": "Prenume",
"last_name": "Nume",
"customer_type": "PF",'
"adress_line": " Str. FN Nr. Bl. Sc. Et. Ap. Sect. Loc. Jud. ",
"cnp": "1970907336523_J77",
"customer_category": "PF_ONLINE",
"open_branch": "213",
"branch_name": "SUCURSALA DEJ",
"insolventa": "NU"
}
]
Sorry for this messy message but is first time when I ask something here.
I wasn't able to build your code as it has a lot of dependencies which I'd need to make all sorts of assumptions about - you may want to read about creating a minimal, reproducible example.
However, I should be able to help you to understand how to use mocks. The first thing to know about mocks is that they should only be used in unit tests, not in your production code. So you wouldn't want to pass a flag into your actual handler to tell it whether or not to run in "mock" mode. Instead, you can create an instance of a mock in your unit test and then inject the mock into the code you're testing, so that your unit test can concentrate on the behaviour of the code you're testing, rather than the behaviour of any external code / web service / database etc that it is dependent on.
Imagine this is the class you want to test (equivalent of your GetCustomerDetailsQueryHandler).
public class MyHandler
{
private readonly IThingToMock thingToMock;
public MyHandler(IThingToMock thingToMock)
{
this.thingToMock = thingToMock;
}
public string MethodToTest(int id)
{
var request = new RequestModel { Id = id };
var response = this.thingToMock.GetInformation(request);
return response.Foo;
}
public async Task<string> MethodToTestAsync(int id)
{
var request = new RequestModel { Id = id };
var response = await this.thingToMock.GetInformationAsync(request).ConfigureAwait(false);
return response.Foo;
}
}
This is dependent on IThingToMock (equivalent of your ICustomerApiClient), which looks like this:
public interface IThingToMock
{
ResponseModel GetInformation(RequestModel request);
Task<ResponseModel> GetInformationAsync(RequestModel request);
}
And IThingToMock's request and response models look like this (I've deliberately kept them really simple):
public class RequestModel
{
public int Id { get; set; }
}
public class ResponseModel
{
public string Foo { get; set; }
}
Before you can start mocking, you need to add a unit test project to your solution (in Visual Studio's new project wizard, choose the template for a xUnit, nUnit or MSTest unit test project - which one you use is a matter of personal preference, I'm using xUnit for this answer).
Next, add a mocking NuGet package to your test project and add the appropriate using directive to your unit tests, e.g. using Moq;. I'm using Moq here, but again, other mocking packages are available, and it's up to you which one you want to use.
Now you can write a unit test, like this:
[Fact]
public void MethodToTest_AnyRequest_ReturnsExpectedResponse()
{
// Arrange
var mockThing = new Mock<IThingToMock>();
var expectedFoo = "Bar";
mockThing.Setup(m => m.GetInformation(It.IsAny<RequestModel>()))
.Returns(new ResponseModel { Foo = expectedFoo });
var handler = new MyHandler(mockThing.Object);
// Act
var actualFoo = handler.MethodToTest(1);
// Assert
Assert.Equal(expectedFoo, actualFoo);
}
Let's break this down. First, we create an instance of Mock<IThingToMock>. This has an Object property, which is an implementation of IThingToMock with all its methods and properties, except that by default the methods do nothing and any members with a return value return the default value for their return type (usually null).
Next, we use the Setup method to make our mock behave in the way we want. In this example, we're saying that its GetInformation method should always return a particular ResponseModel regardless of the properties of the RequestModel which were passed to it. I'm telling it to return a hard-coded value here, but you could tell the mock to return the data deserialized from your JSON file instead.
And then at the end of the Arrange step, we're injecting the mocked IThingToMock into the constructor of the MyHandler class, which is the class we want to test the behaviour of.
To control the behaviour of an asynchronous method, set up the mock using the ReturnsAsync method instead of Returns, for example:
[Fact]
public async Task MethodToTestAsync_AnyRequest_ReturnsExpectedResponse()
{
// Arrange
var mockThing = new Mock<IThingToMock>();
var expectedFoo = "Bar";
mockThing.Setup(m => m.GetInformationAsync(It.IsAny<RequestModel>()))
.ReturnsAsync(new ResponseModel { Foo = expectedFoo });
var handler = new MyHandler(mockThing.Object);
// Act
var actualFoo = await handler.MethodToTestAsync(1);
// Assert
Assert.Equal(expectedFoo, actualFoo);
}
Hopefully these examples give you sufficient insight into how mocks are used in unit tests to be able to apply the principles to your own situation. You may also want to read How YOU can Learn Mock testing in .NET Core and C# with Moq, which provides more examples of how to use Moq, this time with nUnit rather than xUnit, as well as some good practice around unit testing generally. And of course, be sure to consult the documentation for whichever mocking package you're using.
Happy mocking!

More than twenty 'IServiceProvider'. Unit Test

I have this error message :
An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.ManyServiceProvidersCreatedWarning': More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. This is commonly caused by injection of a new singleton service instance into every DbContext instance. For example, calling 'UseLoggerFactory' passing in a new instance each time--see https://go.microsoft.com/fwlink/?linkid=869049 for more details. This may lead to performance issues, consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. This exception can be suppressed or logged by passing event ID 'CoreEventId.ManyServiceProvidersCreatedWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
When I run all Unit Test together
Setup
private readonly DbContextOptions<ApplicationDbContext> _contextOptions;
private readonly DbContextOptions<ApplicationDbContext> _inMemoryContextOptions;
public TestConstructor()
{
// Test for real database READ
_contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
// Test InMemory CREATE UPDATE DELETE
_inMemoryContextOptions = DbContextOptionsBuilder();
SeedInMemoryTestDb(_inMemoryContextOptions);
}
private static DbContextOptions<ApplicationDbContext> DbContextOptionsBuilder()
{
return new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString(),new InMemoryDatabaseRoot())
.Options;
}
Unit Test
[FACT]
public void Test1()
await using var context = new ApplicationDbContext(_contextOptions);
//... Assert.Equal()
[FACT]
public void Test2()
await using var context = new ApplicationDbContext(_inMemoryContextOptions);
//... Assert.Equal()
I have both Setup and Unit Test in 5 or 6 class.
I think I need to re-use the same context for every test but I don't achieve to do that.
[CollectionDefinition("SharedDbContext")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }
public class DatabaseFixture : IDisposable
{
public ApplicationDbContext ApplicationDbContext;
public ApplicationDbContext InMemoryApplicationDbContext;
public DatabaseFixture()
{
// Test for real database READ
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
//// Test InMemory CREATE UPDATE DELETE
var inMemoryContextOptions = DbContextOptionsBuilder();
ApplicationDbContext = new ApplicationDbContext(contextOptions);
InMemoryApplicationDbContext = new ApplicationDbContext(inMemoryContextOptions);
SeedInMemoryTestDb(inMemoryContextOptions);
}
}

DbContext.Set<TEntity> throws null exception in xUnit tests

I have implemented an abstract generic repository that defines a set of methods for performing CRUD operations. I am trying to test them with the help of the Unit of Work pattern in a derived class. I'm using Moq as a testing framework and MockQueryable to enable Async operations like FirstOrDefaultAsync, AddAsync, SaveChangesAsync etc. I am experiencing an error testing the Add method. Here is a dummy project I created to show how things are set up.
DbContext Class
public class MyDBContextClass : DbContext, IDbContext
{
public MyDBContextClass(DbContextOptions options) : base(options) { }
public DbSet<Students> Students { get; set; }
}
Abstract class
public abstract class MyGenericRepository<TEntity> where TEntity : class
{
private readonly IDbContext context;
public MyMyGenericRepository(IDbContext context){
this.context = context;
}
public virtual async Task<int> Add(TEntity Entity)
{
await context.Set<TEntity>().AddAsync(entity); // throws a NullReferenceException during test
await context.SaveChangesAsync();
}
}
Unit of Work Class
public class StudentUnitOfWork : MyGenericRepository<Student>
{
public class StudentUnitOfWork(IDBContext context) : base(context)
{
}
}
The test class
public class StudentUnitOfWorkTest
{
[Fact]
public async Task Add()
{
var students = new List<Student>
{
new Student{ Id = 1, Name = "John Doe"},
new Student{ Id = 2, Name = "Jane Doe"}
}
var mockSet = students.AsQueryable().BuildMockDbSet();
var mockContext = new Mock<IDbContext>();
mockContext.Setup(_ => _.Students).Returns(mockSet.Object);
var sut = new StudentUnitOfWork(mockContext.Object);
var newStudentObj = new Student
{
Name = "Justin Doe"
}
await sut.Add(newStudentObj); // throws a NullReferenceException
mockSet.Verify(_ => _.AddAsync(It.IsAny<Student>(), It.IsAny<CancellationToken>()), Times.Once());
mockContext.Verify(_ => _.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
}
}
Overriding and injecting the base Add method also fails
public override async Task Add(Student student)
{
await base.Add(category); // Still throws a NullReferenceException
}
The test passes when I override the Implementation of Add in the Unit of Work Class and Set the actual dbSet in this case Students.
public class StudentUnitOfWork : MyGenericRepository<Student>
{
public override async Task Add(Student student)
{
await context.Students.AddAsync(student); // Works
await context.SaveChangesAsync();
}
}
Am I missing anything or DbContext.Set only works in production and not tests.
You are not configuring the Set method in IDbContext mock to return anything in your setup code, hence it returns null (default) and an exception is thrown when calling AddAsync.
Update:
Here's the line with error, where you call Set method:
await context.Set<TEntity>().AddAsync(entity);
And Here's your setup code in test, where you skip handling that same Set method:
var mockSet = students.AsQueryable().BuildMockDbSet();
var mockContext = new Mock<IDbContext>();
mockContext.Setup(_ => _.Students).Returns(mockSet.Object);
And since you are not mocking Set method, it returns null when test runs. You obviously cannot call AddAsync method on null, therefore NullReferenceException is thrown.
You should solve it by adding something like that:
mockContext.Setup(_ => _.Set<Student>()).Returns(<DbSet mock object>);
On the other hand,
await context.Students.AddAsync(student);
works, because you have mocked Students property

XUnit: ICollectionFixture instance not getting shared between test methods of a test class

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

Categories

Resources