I am trying to create a unit test project to ensure all the migrations made in a project will successfully allow migrations Up and Down.
I am trying to achieve this through creating two unit tests to do this.
Setup:
NUnit
EntityFramework
LocalDB
FluentMigrator & Runners
This is my setup for the unit tests. I have a connection string that is a link to a LocalDb database (v11) which all these tests use:
[TestFixture]
public class MigrationTestHandler
{
private string ConnectionString
{
get
{
return ConfigurationManager.ConnectionStrings["MigrationDatabase"].ConnectionString;
}
}
[SetUp]
public void SetUp()
{
var blankContext = new DbContext(ConnectionString);
blankContext.Database.Delete();
blankContext.Database.Create();
}
[TearDown]
public void TearDown()
{
var blankContext = new DbContext(ConnectionString);
blankContext.Database.Delete();
}
[Test]
public void CanUpgradeDatabase()
{
var migrator = new MigrationRunnerHandler(ConnectionString);
migrator.Migrate(runner => runner.MigrateUp());
}
[Test]
public void CanRollbackDatabase()
{
var migrator = new MigrationRunnerHandler(ConnectionString);
migrator.Migrate(runner => runner.MigrateUp());
migrator.Migrate(runner => runner.Rollback(int.MaxValue));
}
}
This is the migration runner handler class I use in order to invoke all the migrations:
public class MigrationRunnerHandler
{
private readonly string _connectionString;
private FluentMigrator.Runner.MigrationRunner Runner { get; set; }
public MigrationRunnerHandler(string connectionString)
{
_connectionString = connectionString;
}
private class MigrationOptions : IMigrationProcessorOptions
{
public bool PreviewOnly { get; set; }
public int Timeout { get; set; }
public string ProviderSwitches { get; set; }
}
public void Migrate(Action<IMigrationRunner> runnerAction)
{
var factory = new SqlServer2008ProcessorFactory();
var assembly = Assembly.GetExecutingAssembly();
var announcer = new TextWriterAnnouncer(s => Console.Write(s));
var migrationContext = new RunnerContext(announcer)
{
TransactionPerSession = true,
};
var processor = factory.Create(_connectionString, announcer, new MigrationOptions
{
PreviewOnly = false,
Timeout = 5
});
Runner = new FluentMigrator.Runner.MigrationRunner(assembly, migrationContext, processor);
runnerAction(Runner);
}
}
The problem is that upon TearDown of my tests, FluentMigrator seems to be holding onto a connection to the database. Running sp_who on the database shows that there is a "sleeping" process on the database that is "AWAITING COMMAND" that is left on the database. This means that the TearDown of my test will fail to delete the temporary database as "the database is in use".
Looking through the runner I cannot seem to find a way to close this connection down, I have tried to change the timeouts of all the components involved and also attempted to turn "Pooling" off on the connection string but neither have worked.
Is there a way that I can close down this connection or ensure it is closed down?
Thanks
As IMigrationProcessor implements IDisposable, we should use it like that:
using(var processor = factory.Create(_connectionString, announcer, new MigrationOptions
{
PreviewOnly = false,
Timeout = 5
}))
{
Runner = new FluentMigrator.Runner.MigrationRunner(assembly, migrationContext, processor);
runnerAction(Runner);
}
I also assume, that not disposing the processor is the reason for a hanging connection.
Related
I am using a shared database fixture for my tests, but when running multiple tests at the same time, I get the following error message:
System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
This is my code of my Fixture:
public class SharedDatabaseFixture : IDisposable
{
public static readonly object _lock = new object();
private static bool _databaseInitialized;
private const string postgresConnectionString = "Host=localhost;Database=IntegrationTests; Username=postgres;Password=password";
public SharedDatabaseFixture()
{
Connection = new NpgsqlConnection(postgresConnectionString);
Seed();
Connection.Open();
}
public DbConnection Connection { get; }
public AppDbContext CreateContext(DbTransaction transaction = null!)
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkNpgsql()
.AddMediatR(typeof(IAggregateRoot).Assembly)
.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>))
.AddDbContext<AppDbContext>(options => options.UseNpgsql(Connection))
.BuildServiceProvider();
ServiceLocator.SetLocatorProvider(serviceProvider);
DomainEvents.Mediator = () => ServiceLocator.Current.GetInstance<IMediator>();
var builder = new DbContextOptionsBuilder<AppDbContext>();
builder.UseNpgsql(Connection).UseInternalServiceProvider(serviceProvider);
var context = new AppDbContext(builder.Options);
if (transaction != null)
{
context.Database.UseTransaction(transaction);
}
return context;
}
private void Seed()
{
lock (_lock)
{
if (!_databaseInitialized)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var appDbContextSeed = new AppDbContextSeed(context);
appDbContextSeed.SeedAsync().Wait();
}
_databaseInitialized = true;
}
}
}
public void Dispose() => Connection.Dispose();
}
The code I am testing uses events and those events do queries to the database. Therefore, I am registering some services and also a DbContext.
The problem is, when I run multiple tests at the same time, events are raised at the same time as well and because they are all using the same DbContext, it throws an exception when two handlers try to use the DbContext at the same time.
So, my question is: how can I instantiate a DbContext for each test (but using the same connection) or prevent it from using the DbContext at the same time?
An Example of one of my tests:
public class Project_Create : IClassFixture<SharedDatabaseFixture>
{
public SharedDatabaseFixture Fixture { get; }
public Project_Create(SharedDatabaseFixture fixture) => Fixture = fixture;
[Fact]
public void Creates_succesfully()
{
var project = new Project(SeedConstants.TEST_COMPANY_ID, "ABC", "Hallo123", "2018-123");
Assert.Equal(SeedConstants.TEST_COMPANY_ID, project.CompanyId);
Assert.Equal("ABC", project.Code);
Assert.Equal("Hallo123", project.Description);
Assert.Equal("2018-123", project.Number);
}
}
Project.cs:
public class Project : BaseEntity<Guid, ProjectValidator, Project>, IAggregateRoot
{
public Guid CompanyId { get; private set; }
public string Code { get; private set; }
public string Description { get; private set; }
public string Number { get; private set; }
public Project(Guid companyId, string code, string description, string number)
{
CompanyId = companyId;
Code = code;
Description = description;
Number = number;
Validate(this);
DomainEvents.Raise(new SetCompanyIdEvent(companyId)).GetAwaiter().GetResult();
}
}
As you can see, this project class raises an event. This event has a handler and looks like this:
public class CheckIfProjectIdExistsHandler : INotificationHandler<SetProjectIdEvent>
{
private readonly IAsyncRepository<Project> _projectRepository;
public CheckIfProjectIdExistsHandler(IAsyncRepository<Project> projectRepository)
{
_projectRepository = projectRepository;
}
public async Task Handle(SetProjectIdEvent notification, CancellationToken cancellationToken)
{
var project = await _projectRepository.GetByIdAsync(notification.ProjectId, cancellationToken);
if (project == null)
{
throw new ProjectDoesNotExistsException($"The project with ID {notification.ProjectId} does not exist.");
}
}
}
I hope this illustrates what I am testing
The answer is always simpler than you think.
When adding the DbContext in the Service Provider, I didn't specify the ServiceLifetime, so it is a singleton by default. Changing this to Transient solves the issue. Then the Connection should also be changed by the connectionString, so there are no multiple operations on the same connection.
So, this line:
.AddDbContext<AppDbContext>(options => options.UseNpgsql(Connection))
Should be change like so:
.AddDbContext<AppDbContext>(options => options.UseNpgsql(postgresConnectionString), ServiceLifetime.Transient)
Also, The registration of the repository should be as Transient and not Scoped.
Context
I'm writing unit tests for an API I've been developing and I just ran into an issue while trying to UnitTest a "Context" for accessing a MongoDB Storage.
I abstracted the current interface for my context:
public interface IProjectsContext
{
IMongoCollection<Project> Projects { get; }
}
I'm able to successfully use this interface, together with Moq to UnitTest my Repositories.
However, when trying to UnitTest my Context's implementation I've been unable to muster a solution for mocking the inwards:
public class ProjectsContext : IProjectsContext
{
private const string ProjectsCollectionName = "Projects";
private readonly IDatabaseParameters _dbParams;
private readonly MongoClient _client;
private readonly IMongoDatabase _database;
private IMongoCollection<Project> _projects;
public ProjectsContext(IDatabaseParameters dbParams)
{
_dbParams = dbParams ?? throw new ArgumentNullException(nameof(dbParams));
_client = new MongoClient(_dbParams.ConnectionString);
_database = _client.GetDatabase(_dbParams.DatabaseName);
}
public IMongoCollection<Project> Projects
{
get
{
if (_projects is null)
_projects = _database.GetCollection<Project>(ProjectsCollectionName);
return _projects;
}
}
}
The unit test in question is:
private readonly Fixture _fixture = new Fixture();
private readonly Mock<IDatabaseParameters> _dbParametersMock = new Mock<IDatabaseParameters>();
public ProjectsContextTests()
{
}
[Fact(DisplayName = "Create a Project Context")]
public void CreateProjectContext()
{
// Arrange
_dbParametersMock.Setup(m => m.ConnectionString).Returns(_fixture.Create<string>());
_dbParametersMock.Setup(m => m.DatabaseName).Returns(_fixture.Create<string>());
// Act
var result = new ProjectsContext(_dbParametersMock.Object);
// Assert
result.Should().NotBeNull();
result.Should().BeAssignableTo<IProjectsContext>();
// TODO: Write a test to assert the ProjectCollection
}
Question
The only solution I can think of is changing my ProjectsContext to have a constructor with receives, as a parameter, the IMongoDatabase which is going to be used. However, is this the only solution?
Libraries used
I'm using the following NuGets for my UnitTests and Implementation:
xUnit
Coverlet.msbuild
Moq
AutoFixture
FluentAssertions
MongoDB
ProjectsContext is tightly coupled to implementation concerns/details (ie: MongoClient) that make testing it isolation difficult.
IMongoDatabase is the true dependency and should be explicitly injected into the target class.
Reference Explicit Dependencies Principle
public class ProjectsContext : IProjectsContext {
private const string ProjectsCollectionName = "Projects";
private readonly IMongoDatabase database;
private IMongoCollection<Project> projects;
public ProjectsContext(IMongoDatabase database) {
this.database = database;
}
public IMongoCollection<Project> Projects {
get {
if (projects is null)
projects = database.GetCollection<Project>(ProjectsCollectionName);
return projects;
}
}
}
As for the creation/initialization of the database, that implementation detail can be moved to the composition root
//...ConfigureServices
services.AddScoped<IMongoDatabase>(sp => {
var dbParams = sp.GetRequiredService<IDatabaseParameters>();
var client = new MongoClient(dbParams.ConnectionString);
return client.GetDatabase(dbParams.DatabaseName);
});
//...
Testing of the target class can now be done in isolation without unexpected behavior from 3rd party implementation concerns
[Fact(DisplayName = "Create a Project Context")]
public void CreateProjectContext() {
// Arrange
var collectionMock = Mock.Of<IMongoCollection<Project>>();
var dbMock = new Mock<IMongoDatabase>();
dbMock.Setup(_ => _.GetCollection<Project>(It.IsAny<string>()))
.Returns(collectionMock);
// Act
var result = new ProjectsContext(dbMock.Object);
// Assert
result.Should().NotBeNull()
.And.BeAssignableTo<IProjectsContext>();
//Write a test to assert the ProjectCollection
result.Projects.Should().Be(collectionMock);
}
I've read through the examples on JustMock (we have the paid version FYI) but am having difficulty in regards to updating a record (not adding).
I have a ILogRepo of
public interface ILogRepo
{
DbSet<LogEntry> LogEntries { get; }
LogContext LogContext { get; }
}
I have a LogInteraction of
public class LogInteraction : ILogInteraction
{
private readonly ILogRepo _logRepo;
public LogInteraction(ILogRepo logRepo)
{
_logRepo = logRepo;
}
public void UpdateLog(IList<AckDetail> ackDetails)
{
foreach (var ackDetail in ackDetails)
{
var logRecord = _logRepo.LogEntries.Single(r => r.CheckNum == ackDetail.CheckNo);
logRecord.ProcessingStatus = ackDetail.Result.ToString();
if (!string.IsNullOrWhiteSpace(ackDetail.Message))
{
logRecord.Message = ackDetail.Message;
}
}
_logRepo.LogContext.SaveChanges();
}
}
I've mocked out a fake LogEntries (it's an IList). And last but not least a test of
[TestFixture]
public class LogTests
{
private ILogRepo _mockLogRepo;
private ILogInteraction _uut;
private IList<AckDetail> _fakeAckDetails;
[SetUp]
public void SetUp()
{
//Arrange
_mockLogRepo = Mock.Create<ILogRepo>();
_fakeAckDetails = FakeAckDetails();
_uut = new LogInteraction(_mockLogRepo);
}
[Test]
public void LogUpdated()
{
//Arrange
var expectedResults = FakeLogEntries_AfterAckProcessing();
var expectedJson = JsonConvert.SerializeObject(expectedResults);
_mockLogRepo.Arrange(m => m.LogEntries).IgnoreInstance().ReturnsCollection(FakeLogEntries_AfterSending());
//Act
_uut.UpdateLog(_fakeAckDetails);
var actualResults = _mockLogRepo.LogEntries.ToList();
var actualJson = JsonConvert.SerializeObject(actualResults);
//Assert
Assert.AreEqual(expectedResults.Count, actualResults.Count);
Assert.AreEqual(expectedJson, actualJson);
}
}
In my test, my _mockLogRepo is not being updated by my LogInteraction. Stepping through the code everything seems all well and good. If I inspect the context though and look for changes, that returns a false though. I think I've matched the example on Telerik's site pretty well, but I've only been able to find info on Adding (and by extrapolating, Removing). But since those two are actual methods and Updating isn't in Entity Framework I'm at a bit of a loss. My code will work in production, but I'd like it to work in Test too (kind of the whole point).
I am trying to figure out the reason why my unit test would fail when run together with other unit tests in the solution but pass when run alone. Can anyone show me what am I missing?
The SUT is a class called CompositeClient that is essentially a wrapper class around two other clients. It's main idea is to give priority to one of those clients to be called.
public class CompositeClient : IReceiverChannel
{
private static readonly List<IReceiverChannel> ReceiverChannels = new List<IReceiverChannel>();
public CompositeClient(IReceiverChannel priority, IReceiverChannel normal)
{
ReceiverChannels.Add(priority);
ReceiverChannels.Add(normal);
}
public async Task<IEnumerable<Request>> ReceiveBatchAsync(int batchSize)
{
var req = new List<Request>();
foreach (var channel in ReceiverChannels)
{
req.AddRange(await channel.ReceiveBatchAsync(batchSize - req.Count).ConfigureAwait(false));
if (req.Count >= batchSize)
{
break;
}
}
return req;
}
}
Running the unit test below with all the other unit tests in the solution yield me a failed result. But if I run this test alone, it will pass.
[TestMethod]
public async Task ReceivedRequestShouldComeFromPriorityClientFirst()
{
var normalPriorityClient = GetNormalClientMock();
var highPriorityClient = GetPriorityClientMock();
var compositeClient = new CompositeClient(highPriorityClient, normalPriorityClient);
var requests = await compositeClient.ReceiveBatchAsync(1);
requests.Should().HaveCount(1);
requests.First().Origin.Should().BeSameAs("priority");
normalPriorityClient.CallCount.Should().Be(1); // It will fail here with actual CallCount = 0.
highPriorityClient.CallCount.Should().Be(0);
}
private static ReceiverChannelMock GetNormalClientMock()
{
return new ReceiverChannelMock("normal");
}
private static ReceiverChannelMock GetPriorityClientMock()
{
return new ReceiverChannelMock("priority");
}
private class ReceiverChannelMock : IReceiverChannel
{
private readonly string name;
public ReceiverChannelMock(string name)
{
this.name = name;
}
public int CallCount { get; private set; }
public Task<IEnumerable<Request>> ReceiveBatchAsync(int batchSize)
{
this.CallCount++;
return Task.FromResult<IEnumerable<Request>>(
new List<Request>
{
new Request
{
Origin = this.name
}
});
}
}
Tools used:
Visual Studio 2013
.NET Framework 4.5.2
Resharper 9.2
FluentAssertion
As David pointed out, I overlooked the static field that I declared in the CompositeClient class. Removing the static keyword solved the issue.
My services take a DbContext in their constructor, and I have created a UnitOfWork class that contains all my services in order to make sure the same DbContext is used between them all.
Sample of unitofwork class
private myEntities myContext
public UnitOfWork()
{
myContext = new myEntities();
}
private RequestService requestService;
public RequestService RequestService
{
get
{
if (requestService == null)
requestService = new RequestService(myContext);
return requestService;
}
}
By Using this unitofwork class all the DbContext for my services are now consistent and a change made in one service will appear in another.
However if i need to change the actual Entity context class then that does not get persisted across each service.
Below i have a "Refresh" method that re-initializes it (I need to refresh the context so i can have this class work with some legacy code).
public void Refresh()
{
myContext = new myEntities();
}
However my service classes DbContext objects aren't passed by ref so the context is not set to a new instance of my entity class and this results in the context not being refreshed.
So I think i can solve this by passing by ref as shown below
Service class sample
MyEntities myContext;
public RequestService(ref MyEntities myContext)
{
this.myContext = myContext;
}
However i have seen people say you should not pass context classes by ref so i am curious if there is a better way out there and i am looking at this the wrong way?
Edit
Sorry turns out my proposed solution of passing by ref does not solve my problem, but i am still interested as to how i can update the entity context on the UnitOfWork class e.g. setting it to null and have that effect the service classes.
Never ever should you share DbContext, by reference or as reference. It is not thread safe.
http://msdn.microsoft.com/en-us/data/jj729737.aspx
If you need an easy way to generate multiple DbContext, use ObjectPool from Parallel Extensions Extras.
Update 1
#tia is correct in saying that the private instance will not be updated when original changes:
class Program
{
static void Main(string[] args)
{
var pool1 = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server1"));
var service = new Service(ref pool1);
pool1 = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server2"));
Console.WriteLine(service.Pool.GetObject().ConnectionString);
}
}
class Service
{
private ObjectPool<IDbConnection> connectionPool;
public Service(ref ObjectPool<IDbConnection> pool) { this.connectionPool = pool; }
public ObjectPool<IDbConnection> Pool { get { return connectionPool; } }
}
Will print "Data Source=server 1", even if it would be a static field.
Enter Monostate, a wicked pattern, very similar to Singleton.
class Program
{
static void Main(string[] args)
{
var mop = new MonoObjectPool();
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server1"));
var service = new Service();
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server2"));
Console.WriteLine(service.Pool.GetObject().ConnectionString);
}
}
internal class MonoObjectPool
{
private static ObjectPool<IDbConnection> pool1;
public ObjectPool<IDbConnection> Pool
{
get { return pool1; }
set { pool1 = value; }
}
}
class Service
{
public ObjectPool<IDbConnection> Pool { get { return new MonoObjectPool().Pool; } }
}
I am getting rid of the constructor for service, as I can always get the current IDbConnection generator. There will always be only one instance of it, regardless how many times someone instantiates the MonoObjectPool.
Update 2
The other option might be to use Autofac, but I am not too familiar with it, yet, so I can't give you an example how a type could get resolved in a service instance. Here is a simple example:
class Program
{
private static IContainer container { get; set; }
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<DbCtx1>().As<IDbCtx>();
container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var dbctx = scope.Resolve<IDbCtx>();
Console.WriteLine(dbctx.GetType());
}
builder = new ContainerBuilder();
builder.RegisterType<DbCtx2>().As<IDbCtx>();
container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var dbctx = scope.Resolve<IDbCtx>();
Console.WriteLine(dbctx.GetType());
}
}
}
interface IDbCtx
{
}
class DbCtx1 : IDbCtx { }
class DbCtx2 : IDbCtx { }
Update 3
So going back to the Monostate, this works as expected:
class Program
{
static void Main(string[] args)
{
var mop = new MonoObjectPool();
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server1"));
var service = new Service(mop);
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server2"));
Console.WriteLine(service.Pool.GetObject().ConnectionString);
}
}
internal class MonoObjectPool
{
private static ObjectPool<IDbConnection> pool1;
public ObjectPool<IDbConnection> Pool
{
get { return pool1; }
set { pool1 = value; }
}
}
class Service
{
private MonoObjectPool myPool;
public Service(MonoObjectPool pool) { myPool = pool; }
public ObjectPool<IDbConnection> Pool { get { return myPool.Pool; } }
}
I hope this helps.