Unit Testing SQLite Repository generating No Such Table Error - c#

I have recently built a quiz application for Xamarin.Android, and want to dome some Unit Testing on the shared library.
I am using a repository pattern with the following files.
SQLiteMyAppRepository.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SQLite;
namespace MyApp.DataLayer
{
public class MyAppRepository : IMyAppRepository
{
public static string DatabaseName = "MyApp.sqlite";
private SQLiteConnection _dbConnection;
public MyAppRepository()
{
string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string dbFilePath = Path.Combine(docFolder, DatabaseName);
_dbConnection = new SQLiteConnection(dbFilePath);
}
I have then created a new project to test some of these elements.
The primary issue I am having, is that when I try and run the test, I am getting the following exception:
MyApp.Tests.RepositoryTests.AddQuestion threw
exception:
SQLite.SQLiteException: no such table: Questions
I have been messing about with it for a few hours now and can't seem to find what is wrong, any help would be appreciated. I thought Mocking the repository would get around any issues like this.

The problem here is that you are trying to use a real database in the context of unit tests. One possible solution could be to introduce an additional level of abstraction and separate repository from context(database). This could enable you to properly mock dependencies, e.g.
public interface IMyAppContext
{
IList<Question> GetAllQuestions();
int AddQuestion(Question question);
int UpdateQuestion(Question question);
int DeleteQuestion(Question question);
}
where implementation could be something like this:
public class MyAppContext : IMyAppContext
{
private readonly string _databaseName = "MyApp.sqlite";
private readonly SQLiteConnection _dbConnection;
public MyAppContext()
{
string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string dbFilePath = Path.Combine(docFolder, DatabaseName);
_dbConnection = new SQLiteConnection(dbFilePath);
}
public int AddQuestion(Question question)
{
return _dbConnection.Insert(question);
}
...
}
then inject this one to the repository...
public class MyAppRepository : IMyAppRepository
{
private readonly IMyAppContext _context;
public MyAppRepository(IMyAppContext context)
{
_context = context;
}
public int AddQuestion(Question question)
{
return _context.Insert(question);
}
...
}
Now, after you have done this setup of the unit test should be for example,
[TestMethod]
public void AddQuestion()
{
// Arrange
var contextMock = new Mock<IMyAppContext>();
contextMock.Setup(r => r.AddQuestion(It.IsAny<Question>())).Returns(1);
var sut = new SqLiteAbcdRepository(contextMock.Object);
// Act
var id = sut.AddQuestion(new Question());
// Assert
Assert.AreEqual(1, id);
}

Related

Integration testing with Events - Exceptions when running multiple tests at the same time

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.

How to properly mock MongoDbClient

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

Issue in unit testing and using Moq .Returns

I am new to write Unit Tests. Therefore, I have been struggling with.
I need to insert product via an external WebService. Then the WebService will return a string that is necessary to update the product afterwards.
This is my ApiController:
public class ProductController : ApiController
{
private IProductRepository _ProductRepository;
private IWebService _WebService;
public ProductController(IProductRepository productRepository, IWebService webService)
{
_ProductRepository = productRepository;
_WebService = webService;
}
public HttpResponseMessage Add(string title)
{
using (TransactionScope scope = new TransactionScope())
{
Product product = new Product
{
Title = title
};
this._ProductRepository.Add(product);
// WebService will return a string
string result = this._WebService.Add(product.ID, DateTime.Now);
product.ServiceResult = result;
this._ProductRepository.Update(product);
scope.Complete();
}
return Request.CreateResponse(HttpStatusCode.Created);
}
}
I was wondering how should I write a unit test for this code?
I've tried to write it as follows: (with NUnit, Moq)
[TestFixture]
public class ProductControllerShould : AssertionHelper
{
private Mock<IWebService> _WebService;
private Mock<IProductRepository> _ProductRepository;
[SetUp]
public void Setup()
{
_WebService = new Mock<IWebService>();
_ProductRepository = new Mock<IProductRepository>();
}
[Test]
public void ReturnCreatedOnAdd()
{
_WebService.Setup(b => b.Add(1, DateTime.Now))
.Returns("0");
var controller = new ProductController(_ProductRepository.Object,
_WebService.Object);
var result = controller.Add("Lumia");
Expect(result, Is.EqualTo(HttpStatusCode.Created));
}
}
but when I debug the test, result in this line is null that is not correct.
string result = this._WebService.Add(product.ID, DateTime.Now);
Shouldn't this line handle the behaviour of _WebService.Add() and return "0"?
_WebService.Setup(b => b.Add(1, DateTime.Now))
.Returns("0");
I know I write the test incorrectly but I don't know what should I do.
Thanks.
The problem here, is that you are mocking call of static method `DateTime.Now' . But "Now" in the time point of mocking and as it is called are different. Therefore your call doesn't return anything.
I could suggest 3 following ways:
1) It doesn't really matter for you, if the call was with DateTime.Now or not, in that case you could ignore the second argument:
_WebService.Setup(b => b.Add(1, It.IsAny<DateTime>())).Returns("0");
2) You want to test, that the call was with DateTime.Now. In that case i create an interface for getting DateTime.Now:
public interface IDateTimeNowProvider
{
DateTime Now { get; }
}
public ProductController(IProductRepository productRepository,
IWebService webService,
IDateTimeNowProvider dateTimeNowProvider)
{
_ProductRepository = productRepository;
_WebService = webService;
_dateTimeNowProvider = dateTimeNowProvider;
}
In production code you use default implementation of it, that just returns DateTime.Now. But in your test class you do mock this interface with some predefined values and you test for using this value.
var now = DateTime.Parse("2017-01-22");
var _dateTimeNowProvider = new Mock<IDateTimeNowProvider>();
var controller = new ProductController(_ProductRepository.Object,
_WebService.Object, _dateTimeNowProvider.Object );
_dateTimeNowProvider.Setup(x => x.Now).Returns(now);
_WebService.Setup(b => b.Add(1,now)).Returns("0");
3) You could use special mocking framework that allows to mock static methods, as for example typemock isolator

Fluent Migrator Unit Tests: Holding onto Connection

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.

Passing a SqlConnection to class library file (dll)

I am busy developing a class library project in C# to be reused and attached to different projects in future. It will mainly be used for Table Valued Parameters. My question is, how do I pass a SQL connection to it? The connection will be instantiated in another (main project) that the .dll gets attached to.
I currently have a Class Library Project, and have a Console Application Project created in the same solution for testing purposed.
One last requirement is that I don't want to use ConfigurationManager as the connection string will not be stored in app.config or web.config and by default the queries must be passed back to the calling application.
I've come accross a couple of links like the one below, but nothing I can really use:
Sharing a connection string
Please excuse the noobness, I am 7 weeks into professional programming.
In your dll, simply require an IDbConnection or IDbCommand. All the method is then properly abstracted against the interfaces for the data access.
For example:
In your shared dll
public static int LookUpIntForSomething(IDbConnection connection)
{
using (var command = connection.CreateCommand())
{
// use command.
}
}
In your calling app
using (var connection = new SqlConnection("ConnectionString"))
{
var int = DbQueries.LookupIntForSomething(connection);
}
This is excellent example for dependency injection. I would recommend using enterprise library unity for this kind of stuff. In your data access layer library I would define interface:
public interface IConnectionProvider {
string ConnectionString { get; }
}
public interface IAccountProvider {
Account GetAccountById(int accountID);
}
internal class AccountProvider : IAccountProvider {
private IConnectionProvider _connectionProvider;
public AccountProvider(IConnectionProvider connectionProvider) {
if (connectionProvider == null) {
throw new ArgumentNullException("connectionProvider");
}
_connectionProvider = connectionProvider;
}
public Account GetAccountById(int accountID) {
Account result;
using(var conn = new SqlConnection(connectionProvider)) {
// retrieve result here
}
return result;
}
}
public static class Bootstrapper {
public static void Init() {
ServiceLocator.AddSingleton<IAccountProvider, AccountProvider>();
}
}
Then in any assembly using your data access library you can define implementation for IConnectionProvider, like this:
internal class WebConnectionProvider : IConnectionProvider {
public string ConnectionString { get { return "Server=..."; } }
}
internal static class WebBootstrapper {
public static void Init() {
Bootstrapper.Init();
ServiceLocator.AddSingleton<IConnectionProvider, WebConnectionProvider>();
}
}
And anywhere after you call WebBootstrapper.Init() in your assembly you can use:
var accountProvider = ServiceLocator.Resolve<IAccountProvider>();
accountProvider.GetAccountById(1);
Service locator:
using System;
using Microsoft.Practices.Unity;
public class ServiceLocator {
private IUnityContainer m_Container = new UnityContainer();
public void Add<TFrom, TTo>() where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>();
}
public void BuildUp<T>(T instance) {
m_Container.BuildUp<T>(instance);
}
public void BuildUp(Type type, object instance) {
m_Container.BuildUp(type, instance);
}
public void AddSingleton<TFrom, TTo>() where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
}
public void AddInstance<T>(T instance) {
m_Container.RegisterInstance<T>(instance);
}
public T Resolve<T>() {
return m_Container.Resolve<T>();
}
private static ServiceLocator m_Instance;
public static ServiceLocator Instance {
get { return m_Instance; }
}
static ServiceLocator() {
m_Instance = new ServiceLocator();
}
}
if i understand your requirements correctly,I'm not sure that i do, i would setup a static struct as such
public static struct ConnectionString
{
public int ID;
public string Connection;
public override string ToString()
{
return Connection;
}
public static ConnectionString DataBase1 = new ConnectionString{ ID = 1 , Connection = "YourConnectionStringhere"};
public static ConnectionString DataBase2 = new ConnectionString{ ID = 2 , Connection = "YourConnectionString2here"};
}
and then use it as such
public void SomeMethod()
{
var I = ReferencedDll.DoSomething(ConnectionString.DataBase1.ToString());
}
or
public void SomeMethod()
{
var ClassFromDll = new ReferencedDll.SomeClass(ConnectionString.DataBase1.ToString());
ClassFromDll.DoSomething();
}
of course this leaves your connection strings hard coded which is not ideal

Categories

Resources