I'm having issues in writing a unit test for Get/Add methods on the Entities, not able to pass mocked context inside the DAO class for instantiation.
Its database first approach.
please find the required details below,
My DBContext looks like below
using Microsoft.EntityFrameworkCore;
public class EfDbContext : DbContext
{
private readonly ISqlConnectionProvider _provider;
public EfDbContext(ISqlConnectionProvider provider)
{
_provider = provider;
}
public EfDbContext(DbContextOptions<EfDbContext> options)
: base(options)
{}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
string connectionString = _provider.ConnectionString;
optionsBuilder.UseSqlServer(connectionString);
}
}
public DbSet<ArbReservationEntity> ArbReservationEntities { get; set; }
}
my DAO class looks like below
public class SampleDAO : ISampleDao
{
private readonly ILogger _logHandler;
private readonly EfDbContext _dbContext;
public SampleDAO(ISqlConnectionProvider provider, ILogger logHandler, EfDbContext dbContext)
{
_logHandler = logHandler;
_sqlConnectionProvider = new TestSqlConnectionProvider("test", new AesEncryptDecrypt());
_dbContext = dbContext;
}
public async Task AddAsync(SampleEntity entity)
{
_dbContext.SampleEntities.Add(entity);
await _dbContext.SaveChangesAsync();
}
public async Task<SampleEntity> GetAsync(long uniqueId)
{
var entity = await _dbContext.SampleEntities.FirstOrDefaultAsync(a => a.Id == uniqueId);
return entity;
}
}
my unit tests looks like below:-
using Xunit;
using Moq;
using NSubstitute;
public class SampleDAOTests
{
private ISampleDao _sampleDao;
private readonly ILogger _logger;
private readonly EfDbContext _dbContext;
private readonly ISqlConnectionProvider _sqlConnectionProvider;
public SampleDAOTests()
{
_logger = Substitute.For<ILogger>();
_sqlConnectionProvider = new EncryptedSqlConnectionProvider("test", new AesEncryptDecrypt());
_sampleDao = new SampleDAO(_sqlConnectionProvider, _logger, _dbContext);
}
[Fact]
public async Task GetAsync_Should_Return_SampleEntity()
{
var sampleEntity = new SampleEntity() { };
var sampleEntityList = new List<SampleEntity>()
{
new SampleEntity() { Id = 1, CartId = "1" },
new SampleEntity() { Id = 2, CartId = "2" }
};
var mockSet = new Mock<DbSet<SampleEntity>>();
mockSet.As<IQueryable<SampleEntity>>().Setup(m => m.GetEnumerator()).Returns(() => sampleEntityList.GetEnumerator());
var mockContext = new Mock<EfDbContext>();
_sampleDao = new SampleDAO(_sqlConnectionProvider, _logger, mockContext.Object);
var entities = await _sampleDao.GetAsync(123);
//Assert
Assert.NotNull(entities);
}
}
The below exception is thrown when I try to inject the mockContext inside the sampleDAO class instantiation
_sampleDao = new SampleDAO(_sqlConnectionProvider, _logger, mockContext.Object);
Exception
Message:
System.ArgumentException : Can not instantiate proxy of class: Repository.EF.EfDbContext.
Could not find a parameterless constructor. (Parameter 'constructorArguments')
---- System.MissingMethodException : Constructor on type 'Castle.Proxies.EfDbContextProxy' not found.
Stack Trace:
ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments) line 62
Mock`1.InitializeInstance() line 309
Mock`1.OnGetObject() line 323
Mock.get_Object() line 179
Mock`1.get_Object() line 281
SampleDAOTests.GetAsync_Should_Return_SampleEntity() line 73
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
CodePudding user response:
It's not the best practice to use Moq to wrap datasets. Instead of that it will be better to use either InMemory db provider or Sqlite provider for tests.
First one will cover most of the basic scenarios. Second one will help you in case if you using transactions or other features which are not supported by InMemory.
You can initialize context in test startup, fill it with necessary data and check it's contents after the tested logic will complete execution.