I am trying to apply the new mocking of EF6 to my existing code.
I have a class that Extends DbSet. One of the methods call the base class (BdSet) Create method. Here is the a sample code (not the complete solution or real names):
public class DerivedDbSet<TEntity> : DbSet<TEntity>, IKeyValueDbSet<TEntity>, IOrderedQueryable<TEntity> where TEntity : class
{
public virtual bool Add(string value1, string value2) {
var entity = Create(); // There is no direct implementation of the Create method it is calling the base method
// Do something with the values
this.Add(entity);
return true;
}
}
I am mocking using the Test Doubles sample (here is the peace of code):
var data = new List<DummyEntity> {
new DummyEntity { Value1 = "First", Value2 = "001" },
new DummyEntity { Value1 = "Second", Value2 = "002" }
}.AsQueryable();
var mock = new Mock<DerivedDbSet<DummyEntity>>();
mock.CallBase = true;
mock.As<IQueryable<T>>().Setup(m => m.Provider).Returns(source.Provider);
mock.As<IQueryable<T>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
I've set the CallBase property to true to try to force the call to the base class...
But I keep receiving the following error:
System.NotImplementedException: The member 'Create' has not been implemented on type 'DerivedDbSet1Proxy' which inherits from 'DbSet1'. Test doubles for 'DbSet`1' must provide implementations of methods and properties that are used.
I want the call of create to fallback to the default implementation in DbSet.
Can someone help me with that?
After some struggle with the internal functions and async references of mocking a DbSet I came out with a helper class that solved most of my problems and might serve as base to someone implementation.
Here is the code:
public static class MockHelper
{
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> {
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(); }
public IQueryProvider 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; } }
}
public static Mock<TDbSet> CreateDbSet<TDbSet, TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TDbSet : class, IDbSet<TEntity>
where TEntity : class, new() {
var source = data.AsQueryable();
var mock = new Mock<TDbSet> { CallBase = true };
mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
if (find != null) mock.As<IDbSet<TEntity>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns(find);
return mock;
}
public static Mock<DbSet<TEntity>> CreateDbSet<TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TEntity : class, new() {
return CreateDbSet<DbSet<TEntity>, TEntity>(data, find);
}
}
And here is a use sample (based on the names I've given before):
var data = new List<DummyEntity> {
new DummyEntity { Value1 = "First", Value2 = "001" } },
new DummyEntity { Value1 = "Second", Value2 = "002" } }
};
var mockDummyEntities = MockHelper.CreateDbSet<DerivedDbSet<DummyEntities>, DummyEntities>(data, i => data.FirstOrDefault(k => k.Value2 == (string)i[0]));
var mockContext = new Mock<DummyDbContext>();
mockContext.Setup(c => c.DummyEntities).Returns(mockDummyEntities.Object);
Any suggestions on how to improve this solution is very welcome.
Regards
This is a modified version based on Andre that worked for me. Note, that I did not need the async references. Code will add all derived classes (if any)
Usage:
/// <summary>
///
/// </summary>
[TestMethod]
public void SomeTest()
{
//Setup
var mockContext = new Mock<FakeDbContext>();
//SomeClass can be abstract or concrete
mockContext.createFakeDBSet<SomeClass>();
var db = mockContext.Object;
//Setup create(s) if needed on concrete classes
//Mock.Get(db.Set<SomeOtherClass>()).Setup(x => x.Create()).Returns(new SomeOtherClass());
//DO Stuff
var list1 = db.Set<SomeClass>().ToList();
//SomeOtherClass derived from SomeClass
var subList1 = db.Set<SomeOtherClass>().ToList();
CollectionAssert.AreEquivalent(list1.OfType<SomeOtherClass>.ToList(), subList1);
}
Code:
/// <summary>
/// http://stackoverflow.com/questions/21943328/ef6-mocking-derived-dbsets
/// </summary>
public static class MoqSetupExtensions
{
static IEnumerable<Type> domainTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes());
public static Mock<DbSet<T>> createFakeDBSet<T>(this Mock<FakeDbContext> db, List<T> list = null, Func<List<T>, object[], T> find = null, bool createDerivedSets = true) where T : class
{
list = list ?? new List<T>();
var data = list.AsQueryable();
//var mockSet = MockHelper.CreateDbSet(list, find);
var mockSet = new Mock<DbSet<T>>() { CallBase = true };
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(() => { return data.Provider; });
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(() => { return data.Expression; });
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(() => { return data.ElementType; });
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => { return list.GetEnumerator(); });
mockSet.Setup(m => m.Add(It.IsAny<T>())).Returns<T>(i => { list.Add(i); return i; });
mockSet.Setup(m => m.AddRange(It.IsAny<IEnumerable<T>>())).Returns<IEnumerable<T>>((i) => { list.AddRange(i); return i; });
mockSet.Setup(m => m.Remove(It.IsAny<T>())).Returns<T>(i => { list.Remove(i); return i; });
if (find != null) mockSet.As<IDbSet<T>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>((i) => { return find(list, i); });
//mockSet.Setup(m => m.Create()).Returns(new T());
db.Setup(x => x.Set<T>()).Returns(mockSet.Object);
//Setup all derived classes
if (createDerivedSets)
{
var type = typeof(T);
var concreteTypes = domainTypes.Where(x => type.IsAssignableFrom(x) && type != x).ToList();
var method = typeof(MoqSetupExtensions).GetMethod("createFakeDBSetSubType");
foreach (var item in concreteTypes)
{
var invokeResult = method.MakeGenericMethod(type, item)
.Invoke(null, new object[] { db, mockSet });
}
}
return mockSet;
}
public static Mock<DbSet<SubType>> createFakeDBSetSubType<BaseType, SubType>(this Mock<FakeDbContext> db, Mock<DbSet<BaseType>> baseSet)
where BaseType : class
where SubType : class, BaseType
{
var dbSet = db.Object.Set<BaseType>();
var mockSet = new Mock<DbSet<SubType>>() { CallBase = true };
mockSet.As<IQueryable<SubType>>().Setup(m => m.Provider).Returns(() => { return dbSet.OfType<SubType>().Provider; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.Expression).Returns(() => { return dbSet.OfType<SubType>().Expression; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.ElementType).Returns(() => { return dbSet.OfType<SubType>().ElementType; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.GetEnumerator()).Returns(() => { return dbSet.OfType<SubType>().GetEnumerator(); });
mockSet.Setup(m => m.Add(It.IsAny<SubType>())).Returns<SubType>(i => { dbSet.Add(i); return i; });
mockSet.Setup(m => m.AddRange(It.IsAny<IEnumerable<SubType>>())).Returns<IEnumerable<SubType>>((i) => { dbSet.AddRange(i); return i; });
mockSet.Setup(m => m.Remove(It.IsAny<SubType>())).Returns<SubType>(i => { dbSet.Remove(i); return i; });
mockSet.As<IDbSet<SubType>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>((i) => { return dbSet.Find(i) as SubType; });
baseSet.Setup(m => m.Create<SubType>()).Returns(() => { return mockSet.Object.Create(); });
db.Setup(x => x.Set<SubType>()).Returns(mockSet.Object);
return mockSet;
}
}
Microsoft published a very well explained guide providing an in-memory implementation of DbSet which has a complete implemention for all the methods on DbSet.
Check the article at http://msdn.microsoft.com/en-us/data/dn314431.aspx#doubles
Related
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);
}
I'm trying create unit-tests in ASP.NET Core MVC application with moq. Unfortunately, Nhibernate.ToListAsync() is not supported Linq IQueryable dataset and throw System.NotSupportedException: 'Source Provider must be a INhQueryProvider'.
In this code I mock INhQueryProvider, but it's not enough:
var entities = new List<RequestRole>
{
new RequestRole()
{
Id = 0,
RequestOperator = new RequestOperator() { Id = 1 }
},
new RequestRole()
{
Id = 1,
RequestOperator = new RequestOperator() { Id = 2 }
}
}
.AsQueryable();
// for ToListAsync Mock INhQueryProvider and set it into IQueryable
var queryableProviderMock = new Mock<INhQueryProvider>();
queryableProviderMock.Setup(x => x.ExecuteAsync<IEnumerable<RequestRole>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(entities);
var queryableMock = new Mock<IQueryable<RequestRole>>();
queryableMock.Setup(x => x.Provider).Returns(queryableProviderMock.Object);
queryableMock.Setup(x => x.Expression).Returns(entities.Expression);
queryableMock.Setup(x => x.GetEnumerator()).Returns(entities.GetEnumerator());
queryableMock.Setup(x => x.ElementType).Returns(entities.ElementType);
// mock CreateQuery, without this Linq.Where throwing "System.NotSupportedException: 'Source Provider must be a INhQueryProvider'"
queryableProviderMock.As<INhQueryProvider>()
.Setup(x => x.CreateQuery<RequestRole>(It.IsAny<Expression>()))
.Returns(queryableMock.Object);
var session = new Mock<ISession>();
session.Setup(s => s.Query<RequestRole>()).Returns(queryableMock.Object);
var returns = session.Object.Query<RequestRole>();
// check work
var tolistasync = await returns
.Where(x => x.Id != 0)
.ToListAsync();
In this case Linq.Where conditions not working, because I set same object instead of filtered.
Seems like I should correctly mock INhQueryProvider.CreateQuery, but how?
You'll need to instruct CreateQuery to use the expression. Just returning the mocked queryable isn't going to do anything as you've seen. Additionally CreateQuery is going to need to return an IQueryable with a provider that implements INhQueryProvider. The problem with that is the Provider property doesn't have a setter so you can't set it on an existing queryable.
The way I've solved a similar problem is to create my own sequence where I can set the provider.
Start with creating classes that implement IQueryable<T> and INhQueryProvider; for brevity I'm only implementing what is required to pass the OP use case. Note that CreateQuery<T> returns a queryable with a provider that implements INhQueryProvider:
public class TestingQueryable<T> : IQueryable<T>
{
private readonly IQueryable<T> _queryable;
public TestingQueryable(IQueryable<T> queryable)
{
_queryable = queryable;
Provider = new TestingQueryProvider<T>(_queryable);
}
public Type ElementType => _queryable.ElementType;
public Expression Expression => _queryable.Expression;
public IQueryProvider Provider { get; }
public IEnumerator<T> GetEnumerator()
{
return _queryable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _queryable.GetEnumerator();
}
}
public class TestingQueryProvider<T> : INhQueryProvider
{
public TestingQueryProvider(IQueryable<T> source)
{
Source = source;
}
public IQueryable<T> Source { get; set; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestingQueryable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
return Source.Provider.Execute<TResult>(expression);
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
public int ExecuteDml<T1>(QueryMode queryMode, Expression expression)
{
throw new NotImplementedException();
}
public Task<int> ExecuteDmlAsync<T1>(QueryMode queryMode, Expression expression, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public IFutureEnumerable<TResult> ExecuteFuture<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public IFutureValue<TResult> ExecuteFutureValue<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
Update your query provider setup to use your IQueryable implementation:
queryProviderMock
.Setup(x => x.CreateQuery<RequestRole>(It.IsAny<Expression>()))
.Returns((Expression providedExpression) =>
{
return new TestingQueryable<RequestRole>(queryable.Provider.CreateQuery<RequestRole>(providedExpression));
});
Run .Where(x => x.Id != 0).ToListAsync() and get the expected result:
Working example
You could take it further and just set up the ISession mock to use your IQueryable implementation, do away with mocking the query provider if you don't need to specifically mock it. I don't normally mock what a mock returns if you know what I mean so this would meet my peer review standards.
[Test]
public async Task Test2()
{
var requestRoles = new List<RequestRole>();
requestRoles.Add(new RequestRole { Id = 0, RequestOperator = new RequestOperator { Id = 1 } });
requestRoles.Add(new RequestRole { Id = 1, RequestOperator = new RequestOperator { Id = 2 } });
var sessionMock = new Mock<ISession>();
sessionMock.Setup(s => s.Query<RequestRole>()).Returns(new TestingQueryable<RequestRole>(requestRoles.AsQueryable()));
var query = sessionMock.Object.Query<RequestRole>();
var result = await query.Where(x => x.Id != 0).ToListAsync();
Assert.Multiple(() =>
{
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.Single(), Is.EqualTo(requestRoles.Last()));
});
}
How could I make following concise, with an extension method etc.? Also could that InlineValidator consolidated into the MainPropertyValidator class?
public class PropertyDTOValidator : AbstractValidator<PropertyDTO>
{
public PropertyDTOValidator()
{
RuleFor(p => p).SetValidator(new InlineValidator<PropertyDTO>
{
validator =>
{
return validator.RuleFor(p => p.MainProperty)
.SetValidator(new MainPropertyValidator());
}
}).When(p =>
{
var checkMainProperty = p.MainProperty.Id != -1;
if (!checkMainProperty)
{
// Some actions...
}
return checkMainProperty;
});
}
class MainPropertyValidator : AbstractValidator<PropertyDTO>
{
public MainPropertyValidator()
{
RuleFor(p => p.Id).Must(id =>
{
return id >= 1;
}).WithMessage("MainProperty Id value is not valid...");
}
}
}
Edit
I have following extension but i want to consolidate my validator into a single class like InlineValidator. I checked the code of InlineValidator but i could not consolidate InlineValidator and MainPropertyValidator.
public static class Extensions
{
public static IRuleBuilderOptions<T, TProperty1> SetValidator<T, TProperty1, TProperty2>(this IRuleBuilderInitial<T, TProperty1> ruleBuilder,
Expression<Func<T, TProperty2>> exp, IValidator<TProperty2> validator)
{
return ruleBuilder.SetValidator(new InlineValidator<T>
{
v => v.RuleFor(exp).SetValidator(validator)
} as IValidator<TProperty1>);
}
}
RuleFor(p => p).SetValidator(p => p.MainProperty, new MainPropertyValidator()).When(p =>{});
Alternatively, following extensions could be used;
public static class Extensions
{
public static IRuleBuilderInitial<T, TProperty> When<T, TProperty>(this IRuleBuilderInitial<T, TProperty> rule, Func<T, bool> predicate, ApplyConditionTo applyConditionTo = ApplyConditionTo.AllValidators)
{
return rule.Configure(config =>
{
PropertyRule propertyRule = config;
int num = (int)applyConditionTo;
propertyRule.ApplyCondition(ctx => predicate((T)ctx.InstanceToValidate), (ApplyConditionTo)num);
});
}
public static IRuleBuilderInitial<T, TProperty> Custom<T, TProperty, TProperty2>(this IRuleBuilder<T, TProperty> ruleBuilder, Expression<Func<TProperty, TProperty2>> expression, AbstractValidator<TProperty2> validator)
{
var propChain = PropertyChain.FromExpression(expression);
var propName = propChain.ToString();
var prop = expression.Compile();
Func<string, string, string> joinStr = (s1, s2) => string.Join(".", new[] { s1, s2 }.Where(s => !string.IsNullOrEmpty(s)));
return ruleBuilder.Custom((p, context) =>
{
var val = prop(p);
var validationResult = validator.Validate(val);
propName = joinStr(context.PropertyName, propName);
foreach (var failure in validationResult.Errors)
{
failure.PropertyName = joinStr(propName, failure.PropertyName);
context.AddFailure(failure);
}
});
}
}
RuleFor(p => p).Custom(p => p.MainProperty, new MainPropertyValidator()).When(p =>
{
var checkMainProperty = p.MainProperty.Id != -1;
if (!checkMainProperty)
{
// Some actions...
}
return checkMainProperty;
});
I'm trying to mock this method
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc)
like this
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>())).ReturnsAsync(new { isPair = false });
The method to test doing the call passing an anonymous type to the generic parameter like this
instance.GetResultAsync(u => new {isPair = u == "something" }) //dont look at the function return because as generic could have diferent implementations in many case
Moq never matches my GetResultAsync method with the parameters sent.
I'm using Moq 4
The anonymous type is going to cause you problems. You need a concrete type for this to work.
The following example worked when I changed
instance.GetResultAsync(u => new {isPair = u == "something" })
to
instance.GetResultAsync(u => (object) new {isPair = u == "something" })
Moq is unable to match the anonymous type and that is why you get null when called.
[TestClass]
public class MoqUnitTest {
[TestMethod]
public async Task Moq_Function_With_Anonymous_Type() {
//Arrange
var expected = new { isPair = false };
var iMock = new Mock<IService>();
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>()))
.ReturnsAsync(expected);
var consumer = new Consumer(iMock.Object);
//Act
var actual = await consumer.Act();
//Assert
Assert.AreEqual(expected, actual);
}
public interface IService {
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
}
public class Consumer {
private IService instance;
public Consumer(IService service) {
this.instance = service;
}
public async Task<object> Act() {
var result = await instance.GetResultAsync(u => (object)new { isPair = u == "something" });
return result;
}
}
}
if the code calling the GetResultAsync is dependent on using the anonymous type then what you are trying to do with your test wont work with your current setup. You would probably need to provide a concrete type to the method.
[TestClass]
public class MoqUnitTest {
[TestMethod]
public async Task Moq_Function_With_Concrete_Type() {
//Arrange
var expected = new ConcreteType { isPair = false };
var iMock = new Mock<IService>();
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, ConcreteType>>()))
.ReturnsAsync(expected);
var sut = new SystemUnderTest(iMock.Object);
//Act
var actual = await sut.MethodUnderTest();
//Assert
Assert.AreEqual(expected, actual);
}
class ConcreteType {
public bool isPair { get; set; }
}
public interface IService {
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
}
public class SystemUnderTest {
private IService instance;
public SystemUnderTest(IService service) {
this.instance = service;
}
public async Task<object> MethodUnderTest() {
var result = await instance.GetResultAsync(u => new ConcreteType { isPair = u == "something" });
return result;
}
}
}
I'm using a ViewModelBuilder-class to map my models to ViewModels.
Problem is, when I try to build viewmodels for the Projects property I use a viewmodelbuilder for that too and THAT viewmodelbuilder's model has a reference back to Account which it will also try to map to a viewmodel. Result is stackoverflowexeption.
How to solve this?
I thought of sending a bool to the mapping functions, but then I will lose mapping of some nested properties that i do want to map.
Very thankful for any advice I could get!
public class AccountViewModelBuilder : IViewModelBuilder<AccountViewModel, Account>
{
protected AllViewModelBuilders _allViewModelBuilders { get; private set; }
public AccountViewModelBuilder(AllViewModelBuilders allViewModelBuilders)
{
_allViewModelBuilders = allViewModelBuilders;
}
public AccountViewModel BuildFromEntity(Account entity)
{
return new AccountViewModel()
{
ID = entity.ID,
Name = entity.Name,
//Projects = _allViewModelBuilders.ProjectViewModelBuilder.BuildFromEntity(entity.Projects).ToList()
};
}
public Account BuildFromViewModel(AccountViewModel viewModel, Account entity)
{
if (entity == null) return null;
entity.Name = viewModel.Name;
//entity.Projects = _allViewModelBuilders.ProjectViewModelBuilder.BuildFromViewModel(viewModel.Projects).ToList();
return entity;
}
public IEnumerable<AccountViewModel> BuildFromEntity(ICollection<Account> entities)
{
foreach (var entity in entities)
yield return BuildFromEntity(entity);
}
public IEnumerable<Account> BuildFromViewModel(ICollection<AccountViewModel> viewModels)
{
throw new NotImplementedException();
}
}
Edit
So i decided to go with AutoMapper, with some extensions...
public static class ConditionExtensions
{
public static void IgnoreIfSourceIsNull<T>(this IMemberConfigurationExpression<T> expression)
{
expression.Condition(IgnoreIfSourceIsNull);
}
static bool IgnoreIfSourceIsNull(ResolutionContext context)
{
if (!context.IsSourceValueNull)
{
return true;
}
var result = context.GetContextPropertyMap().ResolveValue(context.Parent);
return result.Value != null;
}
}
public static class AutoMapperConfig
{
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType)
&& x.DestinationType.Equals(destinationType));
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
public static void Configure()
{
Mapper.CreateMap<Project, ProjectViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<ProjectViewModel, Project>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
Mapper.CreateMap<LogbookUser, RegisterViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<RegisterViewModel, LogbookUser>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
Mapper.CreateMap<Account, AccountViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<AccountViewModel, Account>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
Mapper.CreateMap<FileGroup, FileGroupViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<FileGroupViewModel, FileGroup>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
Mapper.CreateMap<AttachedFile, AttachedFileViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<AttachedFileViewModel, AttachedFile>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
Mapper.CreateMap<Customer, CustomerViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<CustomerViewModel, Customer>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
Mapper.CreateMap<WorkOrder, WorkOrderViewModel>().IgnoreAllNonExisting();
Mapper.CreateMap<WorkOrderViewModel, WorkOrder>().IgnoreAllNonExisting().ForAllMembers(c => c.IgnoreIfSourceIsNull());
}
}