How can I mock the Set <entity> method of DbContext? [duplicate] - c#

This question already has answers here:
Moq DbSet NotImplementedException
(2 answers)
Closed 5 years ago.
I want to do unit tests for that generic repository pattern, I have tried it in various ways and I can not get it, the closest thing to do is this here I leave the implementation that I have done
public abstract class Repository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : Entity<TKey>
{
private readonly ValcalContext _context;
private readonly IUnitOfWork _unitOfWork;
public IUnitOfWork UnitOfWork => _unitOfWork;
public Repository(IUnitOfWork uow)
{
_context = uow.Context as ValcalContext;
_unitOfWork = uow;
}
public List<TEntity> All => _context.Set<TEntity>().ToList();
public List<TEntity> AllEager(params Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = _context.Set<TEntity>();
foreach (var include in includes)
{
query = query.Include(include);
}
return query.ToList();
}
public TEntity Find(TKey id)
{
return _context.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return _context.Set<TEntity>().ToList();
}
public void Insert(TEntity item)
{
_context.Entry(item).State = EntityState.Added;
}
public void Delete(TEntity entity)
{
var item = _context.Set<TEntity>().Find(entity.Id);
_context.Set<TEntity>().Remove(item);
}
public void Delete(TKey id)
{
var item = _context.Set<TEntity>().Find(id);
_context.Set<TEntity>().Remove(item);
}
public void Update(TEntity item)
{
_context.Set<TEntity>().Attach(item);
_context.Entry(item).State = EntityState.Modified;
}
public void Dispose()
{
if (_context != null)
_context.Dispose();
}
}
This is my dbContext
public class ValcalContext : DbContext,IValcalContext
{
public ValcalContext() : base("ValcalConnection")
{
}
public static ValcalContext Create()
{
return new ValcalContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
AddConventions(modelBuilder);
var typesToRegister = TypesToRegister();
AddConfigurationsMapping(modelBuilder, typesToRegister);
base.OnModelCreating(modelBuilder);
}
#region Private Methods
/// <summary>
/// /Agrega las convenciones de mapeo a la base de dato
/// </summary>
/// <param name="modelBuilder"></param>
private void AddConventions(DbModelBuilder modelBuilder)
{
modelBuilder.Types().Configure(entity => entity.ToTable(entity.ClrType.Name.ToLowerUnderscored()));
modelBuilder.Conventions.Add(new UnderScoredLowerCaseConvention());
}
private static IEnumerable<Type> TypesToRegister()
{
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !string.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
return typesToRegister;
}
private static void AddConfigurationsMapping(DbModelBuilder modelBuilder, IEnumerable<Type> typesToRegister)
{
foreach (var configurationInstance in typesToRegister.Select(Activator.CreateInstance))
{
modelBuilder.Configurations.Add((dynamic)configurationInstance);
}
}
#endregion
}
I want to do unit tests for that generic repository pattern,for now I have this
[TestClass]
public class RepositoryUnitTest
{
[TestMethod]
public void Sample()
{
//arrange
var mockEntityTest = new Mock<DbSet<EntityTest>>();
var unitOfWork = new Mock<IUnitOfWork>();
var valcalContext = new Mock<ValcalContext>();
valcalContext.Setup(vc => vc.Set<EntityTest>()).Returns(mockEntityTest.Object);
var mock = valcalContext.Object;
unitOfWork.Setup(uow => uow.Context).Returns(mock);
var repository = new RepositoryTest(unitOfWork.Object);
//act
var entityTests = repository.All;
//assert
Assert.AreEqual(entityTests.ToList().Count,0);
}
}
public class RepositoryTest : Repository<EntityTest, int>
{
public RepositoryTest(IUnitOfWork uow) : base(uow)
{
}
}
public class EntityTest : Entity<int>
{
}
but I get this error
I hope you can help me, I've been here for two hours
The member 'IEnumerable.GetEnumerator' has not been
implemented on type 'DbSet1Proxy' which inherits from 'DbSet1'. Test
doubles for 'DbSet`1' must provide implementations of methods and
properties that are used.
This error is different from this post Moq DbSet NotImplementedException
I just tried that solution and I'm still in the same

Really what I did was the following to make it clear
I uploaded the moq version for 4.8
I change this
[TestMethod]
public void Sample()
{
//arrange
var mockEntityTest = new Mock<DbSet<EntityTest>>();
var unitOfWork = new Mock<IUnitOfWork>();
var valcalContext = new Mock<ValcalContext>();
valcalContext.Setup(vc => vc.Set<EntityTest>()).Returns(mockEntityTest.Object);
var mock = valcalContext.Object;
unitOfWork.Setup(uow => uow.Context).Returns(mock);
var repository = new RepositoryTest(unitOfWork.Object);
//act
var entityTests = repository.All;
//assert
Assert.AreEqual(entityTests.ToList().Count,0);
}
for this
[TestMethod]
public void Sample()
{
//arrange
var mockEntityTest = new Mock<DbSet<EntityTest>>();
var list = new List<EntityTest>();
var queryable = list.AsQueryable();
mockEntityTest.As<IQueryable<EntityTest>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockEntityTest.As<IQueryable<EntityTest>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockEntityTest.As<IQueryable<EntityTest>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockEntityTest.As<IQueryable<EntityTest>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
var unitOfWork = new Mock<IUnitOfWork>();
var valcalContext = new Mock<ValcalContext>();
valcalContext.Setup(vc => vc.Set<EntityTest>()).Returns(mockEntityTest.Object);
var mock = valcalContext.Object;
unitOfWork.Setup(uow => uow.Context).Returns(mock);
var repository = new RepositoryTest(unitOfWork.Object);
//act
var entityTests = repository.All;
//assert
Assert.AreEqual(entityTests.ToList().Count,0);
}
and it worked, that can be done generically as explained in this post How do I go about unit testing with Entity Framework and Moq? 45558663 # 45558663
but the essence of what I did was that.

Related

Verifying service calls IRepository method that is defined in IGenericRepository

I have a service let's say Foo service class.
public class FooService : IFooService
{
private readonly IFooRepository _repository;
private readonly ISomeService _eventService;
public FooService(IFooRepository repository, ISomeService eventService)
{
_repository = repository;
_someService = someService;
}
public IReadOnlyCollection<Foo> GetFoos(bool isDeleted = true)
{
var foos= _repository.GetList(x => x.IsDeleted == isDeleted).ToList();
return !foos.Any() ? new List<Foo>(): foos;
}
}
Here is IFooRepository
public interface IFooRepository : IGenericRepository<Foo>
{
}
and here is IGenericRepository
public interface IGenericRepository<T> where T: BaseEntity
{
IReadOnlyCollection<T> GetList(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] nav);
}
In my test I want to verify that FooService's GetFoos method calls GetList method
This is what I tried
[TestClass]
public class FooServiceTest
{
private IQueryable<Foo> _foos;
private Mock<IFooRepository> _fooRepository;
private FooService _fooService;
private Mock<ISomeService> _someService;
[TestInitialize]
public void SetUp()
{
_foos = new List<Foo>
{
new Foo
{
EmailId = "a#a.com",
IsDeleted = false,
},
new Foo
{
EmailId = "a#a.com",
IsDeleted = true,
},
}.AsQueryable();
}
[TestMethod]
public void GetGetFoos_CallsGetList()
{
//Arrange
var foos= _foos.Where(x => x.IsDeleted).ToList();
_fooRepository = new Mock<IFooRepository>();
_fooRepository.Setup(m => m.GetList(x => x.IsDeleted)).Returns(foos);
_someServiceMock = new Mock<ISomeService>();
_fooService = new FooService(_fooRepository.Object, _someServiceMock.Object);
//Act
_fooService.GetFoos(true);
//Assert
_fooRepository.Verify(m=>m.GetList(x=>x.IsDeleted), Times.Once());
}
}
But I get argument null exception in following line
var foos= _repository.GetList(x => x.IsDeleted == isDeleted).ToList();
Any clue why this is happening even though I am saying Returns(foos) during setup.
Also how do I verify the interface method was called?
What is happening (most likely) is Moq can't match Expression<Func<T, bool>> when you do the .Setup().
So instead you can use IsAny<>() approach:
_fooRepository.Setup(m => m.GetList(It.IsAny<Expression<Func<Foo, bool>>>())).Returns(foos);
If you want to assert what expression is passed in, try
Expression<Func<Foo, bool>> capturedExpression = null;
_fooRepository.Setup(m => m.GetList(It.IsAny<Expression<Func<Foo, bool>>>()))
.Returns((Expression<Func<Foo, bool>> e ) => { capturedExpression = e; return foos; });
Assert.IsTrue(capturedExpression.Compile()(_foos[1]));
Assert.IsFalse(capturedExpression.Compile()(_foos[0]));
to verify the method was called you can also change the last it a bit more:
_fooRepository.Setup(m => m.GetList(It.IsAny<Expression<Func<Foo, bool>>>()))
.Returns((Expression<Func<Foo, bool>> e ) => { capturedExpression = e; return foos; })
.Verifiable();
then _fooRepository.Verify(m=>m.GetList(It.IsAny<Expression<Func<Foo, bool>>>()), Times.Once()); However, if it is not called, then capturedExpression is null (that technique is known as implicit assertion)

c# unit testing method that calls EF Core extension method

I've been trying to unit test this simple method:
public void DeleteAllSettingsLinkedToSoftware(Guid softwareId)
{
_dbContext.Settings.Where(s => s.SoftwareId == softwareId).ForEachAsync(s => s.IsDeleted = true);
_dbContext.SaveChanges();
}
However I'm having a hard time unit testing this method from the moment the ForEachAsync() method gets called.
So far I've used Moq to setup the dbContext to return the proper settings when the Where() is executed.
My attempt:
Setup(m => m.ForEachAsync(It.IsAny<Action<Setting>>(), CancellationToken.None));
My question is: How will I unit test the call to the ForEachAsync() method?
I've read online that some people say it's impossible to unit test some static methods, if that's true in my case I'm curious about alternatives to test as much of this method as possible.
Edit
My complete test code:
[TestMethod]
public async Task DeleteAllSettingsLinkedToSoftware_Success()
{
//Arrange
var settings = new List<Setting>
{
new Setting
{
SoftwareId = SoftwareId1
},
new Setting
{
SoftwareId = SoftwareId1
},
new Setting
{
SoftwareId = SoftwareId1
},
new Setting
{
SoftwareId = SoftwareId2
}
}.AsQueryable();
var queryableMockDbSet = GetQueryableMockDbSet(settings.ToList());
queryableMockDbSet.As<IQueryable<Setting>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<Setting>(settings.Provider));
DbContext.Setup(m => m.Settings).Returns(queryableMockDbSet.Object);
_settingData = new SettingData(DbContext.Object, SettingDataLoggerMock.Object);
//Act
var result = await _settingData.DeleteAllSettingsLinkedToSoftwareAsync(SoftwareId1);
//Assert
DbContext.Verify(m => m.Settings);
DbContext.Verify(m => m.SaveChanges());
Assert.AreEqual(4, DbContext.Object.Settings.Count());
Assert.AreEqual(SoftwareId2, DbContext.Object.Settings.First().SoftwareId);
}
I am aware that my Assert still needs more checks.
The GetQueryableMockDbSet method:
public static Mock<DbSet<T>> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(s => sourceList.Add(s));
dbSet.Setup(d => d.AddRange(It.IsAny<IEnumerable<T>>())).Callback<IEnumerable<T>>(sourceList.AddRange);
dbSet.Setup(d => d.Remove(It.IsAny<T>())).Callback<T>(s => sourceList.Remove(s));
dbSet.Setup(d => d.RemoveRange(It.IsAny<IEnumerable<T>>())).Callback<IEnumerable<T>>(s =>
{
foreach (var t in s.ToList())
{
sourceList.Remove(t);
}
});
return dbSet;
}
You don't have to mock ForEachAsync at all. ForEachAsync returns Task and is being execute asynchronously this is the source of your problem.
Use async and await keywards to solve your problem:
public async void DeleteAllSettingsLinkedToSoftware(Guid softwareId)
{
await _dbContext.Settings.Where(s => s.SoftwareId == softwareId)
.ForEachAsync(s => s.IsDeleted = true);
_dbContext.SaveChanges();
}
Edit:
The new exception occurs because the supplied Provider is not a IDbAsyncQueryProvider.
Microsoft implemented a generic version of this interface: TestDbAsyncQueryProvider<TEntity>. Here is the implementation from the link:
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestDbAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new TestDbAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestDbAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
public T Current
{
get { return _inner.Current; }
}
object IDbAsyncEnumerator.Current
{
get { return Current; }
}
}
Now in the Setup you'll have to use it like:
mockSet.As<IQueryable<Setting>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<Setting>(data.Provider));
Disclaimer: I won't provide a direct solution, as requested by OP because I believe this question is a XY problem. Instead I will focus my answer on why is this code so damn hard to test, because yes, more than 30 lines of "arrange" to test 2 lines of code means that something went very wrong.
Short answer
This method doesn't need to be tested, at least at the unit level.
Long answer
The problem with the current implementation is a mix of concern.
The first line: _dbContext.Settings.Where(s => s.SoftwareId == softwareId).ForEachAsync(s => s.IsDeleted = true); contains business logic (s.softwareId == softwareId, s.IsDeleted = true) but also EF logic (_dbContext, ForEachAsync).
The second line: _dbContext.SaveChanges(); contains only EF logic
The point is: such methods (that mix concerns) are hard to test at the unit level. Hence the fact you need mocks and several dozens of "Arrange" code to test only 2 lines of implementation !
Based on this constatation you have 2 options:
You delete your test because the method is mainly containing EF logic which is useless to test (see this great series of articles for more details)
You extract business logic and write a real (and simple) unit test
In the second case, I would implement this logic so that I would be able to write a test like that:
[Test]
public void ItShouldMarkCorrespondingSettingsAsDeleted()
{
var setting1 = new Setting(guid1);
var setting2 = new Setting(guid2);
var settings = new Settings(new[] { setting1, setting2 });
settings.DeleteAllSettingsLinkedToSoftware(guid1);
Assert.That(setting1.IsDeleted, Is.True);
Assert.That(setting1.IsDeleted, Is.False);
}
Easy to write, easy to read.
How about the implementation now ?
public interface ISettings
{
void DeleteAllSettingsLinkedToSoftware(Guid softwareId);
}
public sealed class Settings : ISettings
{
private readonly IEnumerable<Setting> _settings;
public Settings(IEnumerable<Setting> settings) => _settings = settings;
public override void DeleteAllSettingsLinkedToSoftware(Guid softwareGuid)
{
foreach(var setting in _settings.Where(s => s.SoftwareId == softwareId))
{
setting.IsDeleted = true;
}
}
}
public sealed class EFSettings : ISettings
{
private readonly ISettings _source;
private readonly DBContext _dbContext;
public EFSettings(DBContext dbContext)
{
_dbContext = dbContext;
_source = new Settings(_dbContext.Settings);
}
public override void DeleteAllSettingsLinkedToSoftware(Guid softwareGuid)
{
_source.DeleteAllSettingsLinkedToSoftware(softwareGuid);
_dbContext.SaveChanges();
}
}
With a solution like this, each concern is separated which allows to:
Get rid of mocks
Really unit test business-logic code
Gain in maintainability and readability

IDbAsyncQueryProvider in EntityFrameworkCore

I'm using XUNIT to test in a dot net core application.
I need to test a service that is internally making an async query on a DbSet in my datacontext.
I've seen here that mocking that DbSet asynchronously is possible.
The problem I'm having is that the IDbAsyncQueryProvider does not seem to be available in EntityframeworkCore, which I'm using.
Am I incorrect here? Has anyone else got this working?
(Been a long day, hopefully I'm just missing something simple)
EDIT
After asking on GitHub, I got point to this class:
https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Query/Internal/IAsyncQueryProvider.cs
This is what I've gotten to so far in trying to implement this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Query.Internal;
namespace EFCoreTestQueryProvider
{
internal class TestAsyncQueryProvider<TEntity>: IAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
IQueryable CreateQuery(Expression expression)
{
return new TestDbAsyncEnumerable<TEntity>(expression);
}
IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestDbAsyncEnumerable<TElement>(expression);
}
object Execute(Expression expression)
{
return _inner.Execute(expression);
}
TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
{
return Task.FromResult(Execute<TResult>(expression)).ToAsyncEnumerable();
}
Task<TResult> IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, System.Collections.Generic.IAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerable<T>(this.AsEnumerable()).ToAsyncEnumerable();
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator()
{
throw new NotImplementedException();
}
IQueryProvider IQueryable.Provider
{
get { return new TestAsyncQueryProvider<T>(this); }
}
}
}
I've now tried to implement this and have run into some more issues, specifically around these two methods:
public IAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerable<T>(this.AsEnumerable()).ToAsyncEnumerable();
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
I'm hoping that somebody could point me in the right direction as to what I'm doing wrong.
I finally got this to work. They slightly changed the interfaces in EntityFrameworkCore from IDbAsyncEnumerable to IAsyncEnumerable so the following code worked for me:
public class AsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
public AsyncEnumerable(Expression expression)
: base(expression) { }
public IAsyncEnumerator<T> GetEnumerator() =>
new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
public class AsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> enumerator;
public AsyncEnumerator(IEnumerator<T> enumerator) =>
this.enumerator = enumerator ?? throw new ArgumentNullException();
public T Current => enumerator.Current;
public void Dispose() { }
public Task<bool> MoveNext(CancellationToken cancellationToken) =>
Task.FromResult(enumerator.MoveNext());
}
[Fact]
public async Task TestEFCore()
{
var data =
new List<Entity>()
{
new Entity(),
new Entity(),
new Entity()
}.AsQueryable();
var mockDbSet = new Mock<DbSet<Entity>>();
mockDbSet.As<IAsyncEnumerable<Entity>>()
.Setup(d => d.GetEnumerator())
.Returns(new AsyncEnumerator<Entity>(data.GetEnumerator()));
mockDbSet.As<IQueryable<Entity>>().Setup(m => m.Provider).Returns(data.Provider);
mockDbSet.As<IQueryable<Entity>>().Setup(m => m.Expression).Returns(data.Expression);
mockDbSet.As<IQueryable<Entity>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockDbSet.As<IQueryable<Entity>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockCtx = new Mock<SomeDbContext>();
mockCtx.SetupGet(c => c.Entities).Returns(mockDbSet.Object);
var entities = await mockCtx.Object.Entities.ToListAsync();
Assert.NotNull(entities);
Assert.Equal(3, entities.Count());
}
You might be able to clean up those test implementations of the AsyncEnumerable and AsyncEnumerator even more. I didn't try, I just got it to work.
Remember your DbSet on your DbContext needs to be marked as virtual or else you will need to implement some interface wrapper over the DbContext to make this work properly.
I got help from Carsons answer, but I had to alter his code a bit to make it work with EntityFramework Core 6.4.4 and Moq.
Here is the altered code:
internal class AsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
public AsyncEnumerable(Expression expression)
: base(expression) { }
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) =>
new AsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
internal class AsyncEnumerator<T> : IAsyncEnumerator<T>, IAsyncDisposable, IDisposable
{
private readonly IEnumerator<T> enumerator;
private Utf8JsonWriter? _jsonWriter = new(new MemoryStream());
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
Dispose(disposing: false);
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
GC.SuppressFinalize(this);
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_jsonWriter?.Dispose();
_jsonWriter = null;
}
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_jsonWriter is not null)
{
await _jsonWriter.DisposeAsync().ConfigureAwait(false);
}
_jsonWriter = null;
}
public AsyncEnumerator(IEnumerator<T> enumerator) =>
this.enumerator = enumerator ?? throw new ArgumentNullException();
public T Current => enumerator.Current;
public ValueTask<bool> MoveNextAsync() =>
new ValueTask<bool>(enumerator.MoveNext());
}
internal class TestAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new AsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new AsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
A helper class:
public static class MockDbSet
{
public static Mock<DbSet<TEntity>> BuildAsync<TEntity>(List<TEntity> data) where TEntity : class
{
var queryable = data.AsQueryable();
var mockSet = new Mock<DbSet<TEntity>>();
mockSet.As<IAsyncEnumerable<TEntity>>()
.Setup(d => d.GetAsyncEnumerator(It.IsAny<CancellationToken>()))
.Returns(new AsyncEnumerator<TEntity>(queryable.GetEnumerator()));
mockSet.As<IQueryable<TEntity>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<TEntity>(queryable.Provider));
mockSet.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockSet.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
mockSet.Setup(m => m.Add(It.IsAny<TEntity>())).Callback<TEntity>(data.Add);
return mockSet;
}
}
Mocking async unit test (MSTestV2):
[TestMethod]
public async Task GetData_Should_Not_Return_Null()
{
// Arrange
var data = new List<Entity>()
{
new Entity()
};
_mockContext.Setup(m => m.Entitys).Returns(MockDbSet.BuildAsync(data).Object);
// Act
var actual = await _repository.GetDataAsync();
// Assert
Assert.IsNotNull(actual);
}
See here to mock DbContext with async queryable support: https://stackoverflow.com/a/71076807/4905704
Like this:
[Fact]
public void Test()
{
var testData = new List<MyEntity>
{
new MyEntity() { Id = Guid.NewGuid() },
new MyEntity() { Id = Guid.NewGuid() },
new MyEntity() { Id = Guid.NewGuid() },
};
var mockDbContext = new MockDbContextAsynced<MyDbContext>();
mockDbContext.AddDbSetData<MyEntity>(testData.AsQueryable());
mockDbContext.MyEntities.ToArrayAsync();
// or
mockDbContext.MyEntities.SingleAsync();
// or etc.
// To inject MyDbContext as type parameter with mocked data
var mockService = new SomeService(mockDbContext.Object);
}

Conditional registering decorator for open-generic with autofac

I have these open-generics:
public interface IQuery<out TResult> {}
public interface ICacheableQuery<out TResult> : IQuery<TResult> {
string CacheKey { get; }
}
public interface IQueryHandler<in TQuery, out TResult>
where TQuery : IQuery<TResult> {
TResult Handle(TQuery query);
}
and this single decorator:
public class CacheableQueryHandlerDecorator<TQuery, TResult>
: IQueryHandler<TQuery, TResult>
where TQuery : ICacheableQuery<TResult> {
public TResult Handle(TQuery query) {
// doing stuffs....
}
}
What I want is to register decorator only for queries which are implementing ICacheableQuery<out TResult>. I'm registering components like this:
builder.RegisterAssemblyTypes(assemblies)
.AsClosedTypesOf(typeof (IQueryHandler<,>))
.AsImplementedInterfaces()
.Named("queryHandler",typeof(IQueryHandler<,>));
builder.RegisterGenericDecorator(
typeof(CacheableQueryHandlerDecorator<,>),
typeof(IQueryHandler<,>),
fromKey: "queryHandler");
But it registers the decorator for all types. Any idea? Thanks in advance.
Unfortunately, Autofac hasn't got this feature out-of-the-box. However, it can be achieved implementing a RegistrationSource:
public interface IGenericDecoratorRegistrationBuilder : IHideObjectMembers
{
IGenericDecoratorRegistrationBuilder Decorator(Type decoratorType, Func<Type, bool> filter = null, Func<Type, IEnumerable<Parameter>> paramsGetter = null);
}
public static class GenericDecorators
{
public class GenericDecoratorRegistration
{
public Type Type;
public Func<Type, bool> Filter;
public Func<Type, IEnumerable<Parameter>> ParamsGetter;
}
class GenericDecoratorRegistrationBuilder : IGenericDecoratorRegistrationBuilder
{
readonly List<GenericDecoratorRegistration> decorators = new List<GenericDecoratorRegistration>();
public IEnumerable<GenericDecoratorRegistration> Decorators
{
get { return decorators; }
}
public IGenericDecoratorRegistrationBuilder Decorator(Type decoratorType, Func<Type, bool> filter, Func<Type, IEnumerable<Parameter>> paramsGetter)
{
if (decoratorType == null)
throw new ArgumentNullException("decoratorType");
if (!decoratorType.IsGenericTypeDefinition)
throw new ArgumentException(null, "decoratorType");
var decorator = new GenericDecoratorRegistration
{
Type = decoratorType,
Filter = filter,
ParamsGetter = paramsGetter
};
decorators.Add(decorator);
return this;
}
}
class GenericDecoratorRegistrationSource : IRegistrationSource
{
readonly Type decoratedType;
readonly IEnumerable<GenericDecoratorRegistration> decorators;
readonly object fromKey;
readonly object toKey;
public GenericDecoratorRegistrationSource(Type decoratedType, IEnumerable<GenericDecoratorRegistration> decorators, object fromKey, object toKey)
{
this.decoratedType = decoratedType;
this.decorators = decorators;
this.fromKey = fromKey;
this.toKey = toKey;
}
public bool IsAdapterForIndividualComponents
{
get { return true; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
KeyedService ks;
if (swt == null ||
(ks = new KeyedService(fromKey, swt.ServiceType)) == service ||
!swt.ServiceType.IsGenericType || swt.ServiceType.GetGenericTypeDefinition() != decoratedType)
return Enumerable.Empty<IComponentRegistration>();
return registrationAccessor(ks).Select(cr => new ComponentRegistration(
Guid.NewGuid(),
BuildActivator(cr, swt),
cr.Lifetime,
cr.Sharing,
cr.Ownership,
new[] { toKey != null ? (Service)new KeyedService(toKey, swt.ServiceType) : new TypedService(swt.ServiceType) },
cr.Metadata,
cr));
}
DelegateActivator BuildActivator(IComponentRegistration cr, IServiceWithType swt)
{
var limitType = cr.Activator.LimitType;
var actualDecorators = decorators
.Where(d => d.Filter != null ? d.Filter(limitType) : true)
.Select(d => new { Type = d.Type, Parameters = d.ParamsGetter != null ? d.ParamsGetter(limitType) : Enumerable.Empty<Parameter>() })
.ToArray();
return new DelegateActivator(cr.Activator.LimitType, (ctx, p) =>
{
var typeArgs = swt.ServiceType.GetGenericArguments();
var service = ctx.ResolveKeyed(fromKey, swt.ServiceType);
foreach (var decorator in actualDecorators)
{
var decoratorType = decorator.Type.MakeGenericType(typeArgs);
var #params = decorator.Parameters.Concat(new[] { new TypedParameter(swt.ServiceType, service) });
var activator = new ReflectionActivator(decoratorType, new DefaultConstructorFinder(), new MostParametersConstructorSelector(),
#params, Enumerable.Empty<Parameter>());
service = activator.ActivateInstance(ctx, #params);
}
return service;
});
}
}
public static IGenericDecoratorRegistrationBuilder RegisterGenericDecorators(this ContainerBuilder builder, Type decoratedServiceType, object fromKey, object toKey = null)
{
if (builder == null)
throw new ArgumentNullException("builder");
if (decoratedServiceType == null)
throw new ArgumentNullException("decoratedServiceType");
if (!decoratedServiceType.IsGenericTypeDefinition)
throw new ArgumentException(null, "decoratedServiceType");
var rb = new GenericDecoratorRegistrationBuilder();
builder.RegisterCallback(cr => cr.AddRegistrationSource(new GenericDecoratorRegistrationSource(decoratedServiceType, rb.Decorators, fromKey, toKey)));
return rb;
}
}
Sample usage:
public interface IGeneric<T>
{
void SomeMethod();
}
class IntImpl : IGeneric<int>
{
public void SomeMethod() { }
}
class StringImpl : IGeneric<string>
{
public void SomeMethod() { }
}
class GenericDecorator<T> : IGeneric<T>
{
IGeneric<T> target;
public GenericDecorator(IGeneric<T> target)
{
this.target = target;
}
public void SomeMethod()
{
target.SomeMethod();
}
}
static void Configure(ContainerBuilder builder)
{
builder.RegisterType<IntImpl>().Named<IGeneric<int>>("generic");
builder.RegisterType<StringImpl>().Named<IGeneric<string>>("generic");
builder.RegisterGenericDecorators(typeof(IGeneric<>), "generic")
// applying decorator to IGeneric<string> only
.Decorator(typeof(GenericDecorator<>), t => typeof(IGeneric<string>).IsAssignableFrom(t));
}
Please note
You must key the registrations of decorated components because (as far as I know) there's no way to override these with dynamic registrations provided by the RegistrationSource.
In this solution the decorated component inherits the configuration of the decorated one (scoping, sharing, ownership, etc)

Mocking using MOQ and generic types

Method to unit test: GetUserInfo
Following is the class containing the method:
public class AccountService : IAccountService
{
IUnitOfWork _UnitOfWork;
public AccountService(IUnitOfWork unitOfWork)
{
_UnitOfWork = unitOfWork;
}
public UserInfo GetUserInfo(string userName, string password)
{
var userInfo = new UserInfo();
userInfo.UserType = UserType.Invalid;
// Statement of interest
var portalUser = _UnitOfWork.Repository<DvaPortalUser>().Query().Filter(t => t.Email == userName && t.Password == password).Get().FirstOrDefault();
//....Rest of the code is not included for clarity
}
}
The interface to mock IUnitOfWork:
public interface IUnitOfWork
{
void Dispose();
void Save();
void Dispose(bool disposing);
IRepository<T> Repository<T>() where T : class;
}
Repository Implementation:
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
public virtual RepositoryQuery<TEntity> Query()
{
var repositoryGetFluentHelper = new RepositoryQuery<TEntity>(this);
return repositoryGetFluentHelper;
}
internal IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>,
IOrderedQueryable<TEntity>> orderBy = null,
List<Expression<Func<TEntity, object>>>
includeProperties = null,
int? page = null,
int? pageSize = null)
{
IQueryable<TEntity> query = DbSet;
if (includeProperties != null)
includeProperties.ForEach(i => query.Include(i));
if (filter != null)
query = query.Where(filter);
if (orderBy != null)
query = orderBy(query);
if (page != null && pageSize != null)
query = query
.Skip((page.Value - 1)*pageSize.Value)
.Take(pageSize.Value);
return query.ToList();
}
}
RepositoryQuery Implementation:
public sealed class RepositoryQuery<TEntity> where TEntity : class
{
private readonly List<Expression<Func<TEntity, object>>> _includeProperties;
private readonly Repository<TEntity> _repository;
private Expression<Func<TEntity, bool>> _filter;
private Func<IQueryable<TEntity>,
IOrderedQueryable<TEntity>> _orderByQuerable;
private int? _page;
private int? _pageSize;
public RepositoryQuery(Repository<TEntity> repository)
{
_repository = repository;
_includeProperties = new List<Expression<Func<TEntity, object>>>();
}
public RepositoryQuery<TEntity> Filter(Expression<Func<TEntity, bool>> filter)
{
_filter = filter;
return this;
}
public IEnumerable<TEntity> Get()
{
return _repository.Get(
_filter,
_orderByQuerable, _includeProperties, _page, _pageSize);
}
}
Unit Test Method:
[TestMethod]
public void AccountService_GetUserInfo_SuccessfulLogin()
{
var _UnitOfWork = new Mock<IUnitOfWork>();
_AccountService = new AccountService(_UnitOfWork.Object);
_UnitOfWork.Setup(a => a.Repository<T>())).Returns(??); //How do I setup this statement?
_UnitOfWork.VerifyAll();
}
Question: How do I setup mock call for the statement _UnitOfWork.Repository()?
I dont know how your IRepository<T> is implemented, but i gues your Query method returns a IEnumerable<T> or a list.
internal interface IRepository<T>
{
IEnumerable<T> Query();
}
// your mock
_UnitOfWork.Setup(a => a.Repository<DvaPortalUser>())).Returns(() => new MyTestRepository());
1. Solution
Define a explicit implemtation of you repository.
// your implementation
public class MyTestRepository : IRepository<DvaPortalUser>
{
public IEnumerable<DvaPortalUser> Query()
{
// return some test users (mocks)
return new List<DvaPortalUser> {new DvaPortalUser(), new DvaPortalUser()};
}
}
2. Solution (thanks to Yuliam Chandra)
Define a mock instead of the implemtation
var repository = new Mock<IRepository<DvaPortalUser>>();
// return some test users (mocks)
repository.Setup(a => a.Query()).Returns(new[] { new DvaPortalUser() });
_UnitOfWork.Setup(a => a.Repository<DvaPortalUser>()).Returns(repository.Object);
What you choose depends on your solution.

Categories