In this post, we will see how to use In-Memory Database Provider for Unit Test.
We start creating a Blank Solution called ProjectDemo and then, we add a Class Library (.NET Core) called Entities where we will define a class called Retailer:
[RETAILER.CS]
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Entities
{
public class Retailer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string EmailManager { get; set; }
public Retailer(int inputId, string inputName, string inputEmailManager)
{
Id = inputId;
Name = inputName;
EmailManager = inputEmailManager;
}
public Retailer() { }
}
}
Then, we add another Class Library (.NET Core) called Data where we will define the DbContext, the Repositories and the UnitOfWork:
[DATACONTEXT.CS]
using Entities;
using Microsoft.EntityFrameworkCore;
namespace Data
{
public class DataContext : DbContext
{
public DbSet<Retailer> Retailers { get; set; }
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
}
}
[IGENERICREPOSITORY.CS]
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Data.Interfaces
{
public interface IGenericRepository<TEntity> where TEntity : class
{
Task<IEnumerable<TEntity>> GetAll();
Task<TEntity> GetById(int id);
Task<bool> Create(TEntity entity);
bool Update(TEntity entity);
Task<bool> Delete(int id);
}
}
[IRETAILERREPOSITORY.CS]
using Entities;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Data.Interfaces
{
public interface IRetailerRepository
{
Task<bool> InsertRetailer(Retailer objRetailer);
Task<bool> DeleteRetailer(int id);
Task<bool> UpdateRetailer(Retailer objRetailer);
Task<IEnumerable<Retailer>> GetAllRetailers();
Task<Retailer> GetRetailerById(int id);
}
}
[IUNITOFWORK.CS]
using System;
using System.Threading.Tasks;
namespace Data.Interfaces
{
public interface IUnitOfWork : IDisposable
{
IRetailerRepository Retailers { get; }
Task Save();
}
}
[GENERICREPOSITORY.CS]
using Data.Interfaces;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Data.Repositories
{
public abstract class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly DataContext context;
public GenericRepository(DataContext dbContext)
{
this.context = dbContext;
}
public async Task<bool> Create(TEntity entity)
{
try
{
await context.Set<TEntity>().AddAsync(entity);
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<bool> Delete(int id)
{
try
{
var objEntity = await GetById(id);
context.Set<TEntity>().Remove(objEntity);
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<IEnumerable<TEntity>> GetAll()
{
return await context.Set<TEntity>().AsNoTracking().ToListAsync();
}
public async Task<TEntity> GetById(int id)
{
return await context.Set<TEntity>().FindAsync(id);
}
public bool Update(TEntity entity)
{
try
{
context.Set<TEntity>().Update(entity);
return true;
}
catch (Exception)
{
return false;
}
}
}
}
[RETAILERREPOSITORY.CS]
using Data.Interfaces;
using Entities;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Data.Repositories
{
public class RetailerRepository : GenericRepository<Retailer>, IRetailerRepository
{
private readonly DataContext context;
public RetailerRepository(DataContext dbContext) : base(dbContext)
{
this.context = dbContext;
}
public async Task<bool> DeleteRetailer(int id)
{
return await Delete(id);
}
public async Task<IEnumerable<Retailer>> GetAllRetailers()
{
return await GetAll();
}
public async Task<Retailer> GetRetailerById(int id)
{
return await GetById(id);
}
public async Task<bool> InsertRetailer(Retailer objRetailer)
{
return await Create(objRetailer);
}
public async Task<bool> UpdateRetailer(Retailer objRetailer)
{
var inputRetailer = await GetRetailerById(objRetailer.Id);
if (inputRetailer == null)
{
return false;
}
inputRetailer.Name = objRetailer.Name;
inputRetailer.EmailManager = objRetailer.EmailManager;
return Update(inputRetailer);
}
}
}
[UNITOFWORK.CS]
using Data.Interfaces;
using Data.Repositories;
using System.Threading.Tasks;
namespace Data
{
public class UnitOfWork : IUnitOfWork
{
private readonly DataContext _context;
public UnitOfWork(DataContext context)
{
_context = context;
Retailers = new RetailerRepository(_context);
}
public IRetailerRepository Retailers { get; private set; }
public async Task Save()
{
await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
}
Now, we add a Class Library (.NET Core) called EntitiesUI where, we will define an entity called RetailerUI used like output.
We do it for a best practice and to avoid a possible circular dependency:
[RETAILERUI.CS]
namespace EntitiesUI
{
public class RetailerUI
{
public int RetailerId { get; set; }
public string RetailerName { get; set; }
public string RetailerEmailManager { get; set; }
public RetailerUI() { }
public RetailerUI(string inputName, string inputEmailManager)
{
RetailerName = inputName;
RetailerEmailManager = inputEmailManager;
}
}
}
Finally, we add a Class Library (.NET Core) called RetailerCore, where we will define our business layer.
In this project, we will use the library Automapper, a library used to mapped one object to another.
In order to install Automapper, we open the Package Manager Console and we run the command:
Install-Package AutoMapper
[IRETAILERCORE.CS]
using EntitiesUI;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Core.Interfaces
{
public interface IRetailerCore
{
Task<List<RetailerUI>> GetRetailers();
Task<RetailerUI> GetRetailerById(int id);
Task<bool> InsertRetailer(RetailerUI objRetailerUI);
Task<bool> UpdateRetailer(RetailerUI objRetailerUI);
Task<bool> DeleteRetailer(int id);
}
}
[RETAILERCORE.CS]
using AutoMapper;
using Core.Interfaces;
using Data.Interfaces;
using Entities;
using EntitiesUI;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Core
{
public class RetailerCore : IRetailerCore, IDisposable
{
private readonly IUnitOfWork _unitOfWork;
private readonly IMapper iMapper;
public RetailerCore(IUnitOfWork inputUnitOfWork)
{
_unitOfWork = inputUnitOfWork;
ConfigurationAutomapper();
}
private void ConfigurationAutomapper()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Retailer, RetailerUI>()
.ForMember(destination => destination.RetailerId, opts => opts.MapFrom(source => source.Id))
.ForMember(destination => destination.RetailerName, opts => opts.MapFrom(source => source.Name))
.ForMember(destination => destination.RetailerEmailManager, opts => opts.MapFrom(source => source.EmailManager))
.ReverseMap();
});
iMapper = config.CreateMapper();
}
public async Task<bool> DeleteRetailer(int id)
{
var objRetailer = await _unitOfWork.Retailers.GetRetailerById(id);
if (objRetailer == null)
{
return false;
}
try
{
await _unitOfWork.Retailers.DeleteRetailer(id);
await _unitOfWork.Save();
return true;
}
catch
{
return false;
}
}
public void Dispose()
{
_unitOfWork.Dispose();
}
public async Task<RetailerUI> GetRetailerById(int id)
{
var objRetailer = await _unitOfWork.Retailers.GetRetailerById(id);
if (objRetailer == null)
{
return null;
}
else
{
return iMapper.Map<Retailer, RetailerUI>(objRetailer);
}
}
public async Task<List<RetailerUI>> GetRetailers()
{
var lstRetailers = await _unitOfWork.Retailers.GetAllRetailers();
return iMapper.Map<IEnumerable<Retailer>, List<RetailerUI>>(lstRetailers);
}
public async Task<bool> InsertRetailer(RetailerUI objRetailerUI)
{
try
{
await _unitOfWork.Retailers.InsertRetailer(iMapper.Map<RetailerUI, Retailer>(objRetailerUI));
await _unitOfWork.Save();
return true;
}
catch
{
return false;
}
}
public async Task<bool> UpdateRetailer(RetailerUI objRetailerUI)
{
var objRetailer = await _unitOfWork.Retailers.GetRetailerById(objRetailerUI.RetailerId);
if (objRetailer == null)
{
return false;
}
else
{
objRetailer.Name = objRetailerUI.RetailerName;
objRetailer.EmailManager = objRetailerUI.RetailerEmailManager;
await _unitOfWork.Retailers.UpdateRetailer(objRetailer);
try
{
await _unitOfWork.Save();
return true;
}
catch
{
return false;
}
}
}
}
}
We have done and we should have a project like that:
Now, we add a xUnit Test Project (.NET Core) called UnitTests that we will use for testing RetailerCore.
In order to install “In Memory Database Provider”, we open the Package Manager Console and run the command:
Install-Package Microsoft.EntityFrameworkCore.InMemory
After the installation, we define a class called RetailerUnitTests where we will create all Unit Tests:
[RETAILERUNITTESTS.CS]
using Core;
using Data;
using Entities;
using EntitiesUI;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace UnitTests
{
public class RetailerUnitTests
{
DataContext objDataContext;
UnitOfWork objUnitOfWork;
RetailerCore objRetailerCore;
public RetailerUnitTests()
{
DbContextOptions<DataContext> options;
// definition of Database in memory
var builder = new DbContextOptionsBuilder<DataContext>().UseInMemoryDatabase(databaseName: "TestDB")
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
objDataContext = new DataContext(builder.Options);
objUnitOfWork = new UnitOfWork(objDataContext);
objRetailerCore = new RetailerCore(objUnitOfWork);
Task.Run(() => FeedDbInMemory()).Wait();
}
private async Task FeedDbInMemory()
{
var lstRetailer = await objUnitOfWork.Retailers.GetAllRetailers();
foreach (var item in lstRetailer)
{
await objUnitOfWork.Retailers.DeleteRetailer(item.Id);
await objUnitOfWork.Save();
}
#region Ratailer
Retailer objRetailer = null;
for (var i = 1; i <= 20; i++)
{
objRetailer = new Retailer(i, $"Name{i.ToString()}", $"Email{i.ToString()}");
await objUnitOfWork.Retailers.InsertRetailer(objRetailer);
await objUnitOfWork.Save();
}
#endregion
}
#region RetailerRepository Tests
[Fact]
public async Task InsertRetailer_ReturnTrue()
{
var objRetailer = new Retailer(100, "Name100", "Email100");
var result = await objUnitOfWork.Retailers.InsertRetailer(objRetailer);
await objUnitOfWork.Save();
var insertedRetailer = await objUnitOfWork.Retailers.GetRetailerById(100);
Assert.True(result);
Assert.Equal("Email100", insertedRetailer.EmailManager);
Assert.Equal("Name100", insertedRetailer.Name);
}
[Fact]
public async Task GetRetailerById_RetailerIdDoesNotExist_ReturnARetailerObjectNull()
{
var foundRetailer = await objUnitOfWork.Retailers.GetRetailerById(30);
Assert.Null(foundRetailer);
}
[Fact]
public async Task GetRetailerById_RetailerIdExist_ReturnARetailerObject()
{
var insertedRetailer = await objUnitOfWork.Retailers.GetRetailerById(3);
Assert.Equal(typeof(Retailer).FullName, insertedRetailer.GetType().FullName);
Assert.Equal("Email3", insertedRetailer.EmailManager);
Assert.Equal("Name3", insertedRetailer.Name);
}
[Fact]
public async Task GetUpdateRetailer_RetailerExists_ReturnTrue()
{
Retailer objRetailer = new Retailer { Id = 3, EmailManager = "NewEmail3", Name = "NewName3" };
var result = await objUnitOfWork.Retailers.UpdateRetailer(objRetailer);
await objUnitOfWork.Save();
var updatedRetailer = await objUnitOfWork.Retailers.GetRetailerById(3);
Assert.True(result);
Assert.Equal("NewEmail3", updatedRetailer.EmailManager);
Assert.Equal("NewName3", updatedRetailer.Name);
}
[Fact]
public async Task GetUpdateRetailer_RetailerDoesNotExist_ReturnFalse()
{
Retailer objRetailer = new Retailer { Id = 30, EmailManager = "NewEmail3", Name = "NewName3" };
var result = await objUnitOfWork.Retailers.UpdateRetailer(objRetailer);
await objUnitOfWork.Save();
Assert.False(result);
}
[Fact]
public async Task DeleteRetailer_RetailerExists_ReturnTrue()
{
int retailerID = 3;
var result = await objUnitOfWork.Retailers.DeleteRetailer(retailerID);
await objUnitOfWork.Save();
Assert.True(result);
}
[Fact]
public async Task DeleteRetailer_RetailerDoesNotExis_ReturnFalse()
{
int retailerID = 30;
var result = await objUnitOfWork.Retailers.DeleteRetailer(retailerID);
await objUnitOfWork.Save();
Assert.False(result);
}
[Fact]
public async Task GetAllRetailer_ReturnAListOfRetailers()
{
await FeedDbInMemory();
var lstRetailers = await objUnitOfWork.Retailers.GetAllRetailers();
Assert.Equal(typeof(List<Retailer>).FullName, lstRetailers.GetType().FullName);
Assert.Equal(20, lstRetailers.Count());
}
#endregion
#region RetailerCore Tests
[Fact]
public async Task GetRetailers_ReturnAListOfRetailerUI()
{
List<RetailerUI> lstRetailersUI = null;
lstRetailersUI = await objRetailerCore.GetRetailers();
Assert.Equal(typeof(List<RetailerUI>).FullName, lstRetailersUI.GetType().FullName);
Assert.Equal(20, lstRetailersUI.Count());
}
[Fact]
public async Task GetRetailerById_RetailerIdDoesNotExist_ReturnARetailerUIObjectNull()
{
int inputId = 30;
var findRetailerUI = await objRetailerCore.GetRetailerById(inputId);
Assert.Null(findRetailerUI);
}
[Fact]
public async Task GetRetailerById_RetailerIdExist_ReturnARetailerUIObject()
{
int inputId = 3;
var objRetailerUI = await objRetailerCore.GetRetailerById(inputId);
Assert.Equal(typeof(RetailerUI).FullName, objRetailerUI.GetType().FullName);
Assert.Equal("Email3", objRetailerUI.RetailerEmailManager);
Assert.Equal("Name3", objRetailerUI.RetailerName);
}
[Fact]
public async Task InsertRetailerUI_ReturnTrue()
{
var objRetailerUI = new RetailerUI("Name100", "Email100");
var result = await objRetailerCore.InsertRetailer(objRetailerUI);
var lstRetailers = await objRetailerCore.GetRetailers();
var objRetailerUICreated = lstRetailers.Where(ll => ll.RetailerName == "Name100").SingleOrDefault();
Assert.True(result);
Assert.Equal("Email100", objRetailerUICreated.RetailerEmailManager);
Assert.Equal("Name100", objRetailerUICreated.RetailerName);
}
[Fact]
public async Task GetUpdateRetailerUI_RetailerUIExists_ReturnTrue()
{
RetailerUI objRetailerUI = await objRetailerCore.GetRetailerById(3);
objRetailerUI.RetailerName = "NewName1000";
objRetailerUI.RetailerEmailManager = "NewEmail1000";
var result = await objRetailerCore.UpdateRetailer(objRetailerUI);
var updatedRetailer = await objRetailerCore.GetRetailerById(3);
Assert.True(result);
Assert.Equal("NewEmail1000", updatedRetailer.RetailerEmailManager);
Assert.Equal("NewName1000", updatedRetailer.RetailerName);
}
[Fact]
public async Task GetUpdateRetailerUI_RetailerUIDoesNotExist_ReturnFalse()
{
RetailerUI objRetailerUI = await objRetailerCore.GetRetailerById(3);
objRetailerUI.RetailerId = 1000;
objRetailerUI.RetailerName = "NewName1000";
objRetailerUI.RetailerEmailManager = "NewEmail1000";
var result = await objRetailerCore.UpdateRetailer(objRetailerUI);
Assert.False(result);
}
[Fact]
public async Task DeleteRetailerUI_RetailerExists_ReturnTrue()
{
int retailerID = 3;
var result = await objRetailerCore.DeleteRetailer(retailerID);
Assert.True(result);
}
[Fact]
public async Task DeleteRetailerUI_RetailerDoesNotExist_ReturnFalse()
{
int retailerID = 3000;
var result = await objRetailerCore.DeleteRetailer(retailerID);
Assert.False(result);
}
#endregion
}
}
Now, we open Test Explorer and we run all tests: