Background: I am writing tests around services that make use of ef core. I want to use sqllite as it is relational.
I have written a base class for tests that will use a mock db factory I wrote to setup basic generic things like http mocking and the DAL.
namespace Bll.UnitTests
{
public class TestBase : IDisposable
{
// pass httpclient as dependency, setup messageHandler for stubbing
protected HttpClient httpClient;
protected Mock<HttpMessageHandlerFake> fakeHttpMessageHandler = new Mock<HttpMessageHandlerFake> { CallBase = true };
protected Mock<Logger> loggerMock;
protected DalContext dataContext;
protected MockDbFactory mockDbFactory;
public TestBase()
{
mockDbFactory = new MockDbFactory();
httpClient = new HttpClient(fakeHttpMessageHandler.Object);
dataContext = mockDbFactory.testDb;
loggerMock = new Mock<Logger>(dataContext);
}
public void Dispose()
{
mockDbFactory.Dispose();
}
}
}
Here is my mock db factory that should just setup a connection in memory and seems to work.
using Dal;
using Microsoft.EntityFrameworkCore;
using Moq;
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
namespace Bll.UnitTests.Factories
{
// In-memory database only exists while the connection is open
public class MockDbFactory : IDisposable
{
private SqliteConnection connection;
public DalContext testDb;
public MockDbFactory()
{
OpenConnection();
testDb = GetTestDb();
}
public void Dispose()
{
CloseConnection();
}
private void OpenConnection()
{
connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
}
private void CloseConnection()
{
connection.Close();
}
private DalContext GetTestDb()
{
var options = new DbContextOptionsBuilder<DalContext>()
.UseSqlite(connection)
.Options;
// Create the schema in the database
using (var context = new DalContext(options))
{
context.Database.EnsureCreated();
return context;
}
}
}
}
In my test class datacontext is disposed when I debug my service under test.
public class LocationServiceTest : TestBase
{
private LocationService sut;
public LocationServiceTest(): base()
{
sut = new LocationService(
httpClient,
loggerMock.Object,
dataContext
);
}
[Fact]
public async Task UpdateCountriesAsync_CallsCountryApiAndUpdatesDatabase()
{
// arrange
// setup get country data to return 2 countries
var temp = BuildCountryApiReturnable(2);
fakeHttpMessageHandler.Setup(f => f.Send(It.IsAny<HttpRequestMessage>())).Returns(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(temp)
});
// act
try
{
var result = await sut.UpdateCountriesAsync();
// assert
Assert.True(dataContext.Country.Count() == 2);
Assert.True(dataContext.Location.Count() == 2);
}
catch(Exception e)
{
throw e;
}
}
I think I understand that a using statement is necessary as this will create my connection and dispose of it, but I am trying to do that manually so that I can inject the data context into my service. If I have to wrap everything in a using statement I will be forced to change my service..
To answer your question: In your
MockDbFactory
, you already disposed the context by theusing
clause:You should initiate a new instance of
DalContext
and handle its disposal in yourMockDbFactory.Dispose
method instead: