Mock Expression Parameter to Repository Method - c#

Using Moq. I have a repository with the following interface:
public virtual IEnumerable<TEntity> GetBySpec(ISpecification<TEntity> specification, params string[] includes)
{
IQueryable<TEntity> query = includes.Aggregate<string, IQueryable<TEntity>>(_dbSetQuery, (current, include) => current.Include(include));
return query.Where(specification.SatisfiedBy())
.AsEnumerable<TEntity>();
}
In this case, i'm using a DirectSpecification:
public sealed class DirectSpecification<TEntity> : Specification<TEntity>
{
Expression<Func<TEntity, bool>> _MatchingCriteria;
public DirectSpecification(Expression<Func<TEntity, bool>> matchingCriteria)
{
_MatchingCriteria = matchingCriteria;
}
public override Expression<Func<TEntity, bool>> SatisfiedBy()
{
return _MatchingCriteria;
}
}
In my actual code i'm calling
var recentlyChanged = _vwPersonRepository.GetBySpec(
new DirectSpecification<vwPerson>(person =>
person.ModifiedDate > modifiedFromDay &&
person.ModifiedDate < modifiedTo));
var recentlyCreated = _vwPersonRepository.GetBySpec(
new DirectSpecification<vwPerson>(person =>
person.CreatedDate > createdFromDay &&
person.CreatedDate < createdTo));
Edit: As suggested by duplicate, I've tried this:
Container.GetMock<IvwPersonRepository>()
.Setup(p => p.GetBySpec(It.IsAny<ISpecification<vwPerson>>()))
.Returns((Expression<Func<vwPerson, bool>> predicate) =>
items.Where(predicate));
I get a
Exception thrown: 'System.Reflection.TargetParameterCountException' in mscorlib.dll
Additional information: Parameter count mismatch.
My question is complicated by having the ISpecification parameter, how can I get the correct parameters so I can work with the predicate?
Edit 2: Thanks to Patrick, here is the solution:
Container.GetMock<IvwPersonRepository>()
.Setup(p => p.GetBySpec(It.IsAny<ISpecification<vwPerson>>(), It.IsAny<string[]>()))
.Returns((ISpecification<vwPerson> specification, string[] includes) =>
items.Where(predicate));
They key was to include the string[] includes, even though I don't pass it as a parameter the reflection finds it and expects it to be there.
Brilliant!

The Setup call in your edit is wrong, it should be:
Container.GetMock<IvwPersonRepository>()
.Setup(p => p.GetBySpec(It.IsAny<ISpecification<vwPerson>>()))
.Returns((ISpecification<vwPerson> specification) => /* TODO */);
(This is because the parameters passed to Returns are the parameters passed to the function being setup, which in this case is GetBySpec.)
I believe (based on what you posted) you could just do this:
Container.GetMock<IvwPersonRepository>()
.Setup(p => p.GetBySpec(It.IsAny<ISpecification<vwPerson>>()))
.Returns((ISpecification<vwPerson> specification) => items.Where(specification.SatisfiedBy()));
However, you might see some benefit by using a factory to create your specifications so that you can mock them to avoid relying on their implementation (in the call to SatisfiedBy above).

Related

Entity Framework Core Repository Pattern Eager Load

I am trying to build out a web application using a repository pattern. In doing so, I have noticed that my navigation properties are not loading. I was able to get my properties to load using EF Proxies, but when I went to use my DTOs, it really didn't like that. I have since removed all the Proxy bit of code and decided that I just need to provide a way to Eager Load the data I care about.
In my repository, I have an interface similar to this. Using DBSet, I am able to use the Find method to generically find the value and return it from the repo. Doing this, I am not able to pass in Includes to resolve the navigation properties.
public async Task<T> GetById(int Id)
{
var query = await this.Context.Set<T>().FindAsync(Id);
// Left split like this for clarity
return query;
}
The only way I got this working was by removing the generic piece and adding a new method called GetUserById into the UserRepository.
public class UserRepository : RepositoryBase<User>, IUserRepository
{
private readonly Context _context;
public UserRepository(Context context) : base(context) {
_context = context;
}
public User GetUserById(int Id)
{
var query = _context.Users.Include("Department").Where(x => x.Id == Id).FirstOrDefault();
return query;
}
}
Please tell me there is a better way.
Try this GetUserById implementation:
public User GetUserById(int Id, params Expression<Func<User, object>>[] includes)
{
var query = _context.Users.Where(x => x.Id == Id);
foreach (var include in includes)
{
query.Include(include);
}
return query.FirstOrDefault();
}
And this is how you would call it:
var repo = new UserRepository();
var user = repo.GetUserById(100, u => u.Department);

How do I mock an AbstractValidator that is using rule sets?

I have an abstract validator, with the following structure:
public abstract class RiskAssessmentServiceCreateRequestValidator<T>
: AbstractValidator<T> where T
: IRiskAssessmentServiceCreateRequest
{
public RiskAssessmentServiceCreateRequestValidator(ApplicationContext context)
{
RuleSet("modelBinding", () =>
{
RuleFor(x => x.ServiceProviderId).NotNull().GreaterThan(0);
});
RuleSet("handler", () =>
{
//....
});
}
}
In my request handler I am calling a derived instance of this class like that:
var validationResult = _validator.Validate(request, ruleSet: "handler");
How can I mock that particular call to Validate in my unit tests? If I would not use the rule sets, my Setup would look like this:
_validator.Setup(x => x.Validate(It.IsAny<CreateRequest>()))
.Returns(validationResult);
The following call is not allowed, since optional parameters are not allowed in an expression tree:
_validator.Setup(x => x.Validate(
It.IsAny<CreateRequest>(),
ruleSet: It.IsAny<string>()))
.Returns(validationResult);
Theoretically I could set it up like this:
_validator.Setup(x => x.Validate(
It.IsAny<CreateRequest>(),
(IValidatorSelector)null,
It.IsAny<string>()))
.Returns(validationResult);
But this then results in:
System.NotSupportedException : Unsupported expression: x => x.Validate<CreateRequest>(It.IsAny<CreateRequest>(), null, It.IsAny<string>())
Extension methods (here: DefaultValidatorExtensions.Validate) may not be used in setup / verification expressions.
Except from using the real validator, which I want to avoid, how can I resolve this and setup Moq in a suitable way?
There are really two questions here.
The first is how to mock with optional parameters - Simply treat optional parameters are non-optional.
However, you are trying to mock an extension method, that is not possible. Instead, you need to mock the method that the extension is trying to call. A cursory glance at the source, and I think that under the hood it is calling validator.Validate(ValidationContext) so your Moq code could be something like this:
_validator
.Setup(x => x.Validate(It.IsAny<ValidationContext<CreateRequest>>())
.Returns(validationResult);
Try
var mock = new Mock<AbstractValidator<object>>();
mock.Setup(x => x.Validate(It.Is<ValidationContext<object>>(ctx => IsExpectedRuleSet(ctx, new[] { "Rule1", "Rule2" }))))
.Return(...);
mock.Object.Validate(new object(), ruleSet: "Rule1,Rule2");
bool IsExpectedRuleSet(ValidationContext context, string[] expectedRuleSet)
{
return (context.Selector as FluentValidation.Internal.RulesetValidatorSelector)?.RuleSets.SequenceEqual(expectedRuleSet) == true;
}

Unit testing using Moq and setting up GetAll with include

I have this strange problem with MOQ and I cant use the GetAll('include') method to test my controller.
My Tests initialization
// GetAll
menusDb.Setup(m => m.GetAll()).Returns(menus.AsQueryable());
menusDb.Setup(m => m.GetAll(It.IsAny<Expression<Func<Menu, object>>>()))
.Returns((Expression<Func<Menu,object>> pred) => {
return menus.AsQueryable();
});
// FindByIdAsync
menusDb.Setup(m => m.FindByByIdAsync(It.IsAny<int>()))
.Returns((int x) => Task.FromResult(menus.Find(m => m.ID == x)));
menusDb.Setup(m => m.FindByByIdAsync(It.IsAny<int>(), It.IsAny<Expression<Func<Menu, object>>[]>()))
.Returns((int x, Expression<Func<Menu,
object>>[] includeProperties) => Task.FromResult(menus.Find(m => m.ID == x)));
Now whenever I am trying to test
_menusDB.GetAll(s=>s.Sections)
The moq version of menusdb.getAll() method is not fired at all
All other Moq methods are triggered correctly...
examples
_menusDB.GetAll();
_menusDB.FindByByIdAsync(id,
m => m.Sections.Select(s => s.Image),
m => m.Sections.Select(s => s.MenuItems.Select(mi => mi.Image)));
And these are the Getall and find functions with includes in my generic repository.
public IQueryable<TEntity> GetAll<TProperty>(Expression<Func<TEntity, TProperty>> propertyToInclude) {
return ObjectSet.Include(propertyToInclude);
}
public async Task<TEntity> FindByByIdAsync(int id, params Expression<Func<TEntity, object>>[] propertiesToInclude) {
var query = propertiesToInclude.Aggregate(ObjectSet as IQueryable<TEntity>, (current, property) => current.Include(property));
return await query.SingleOrDefaultAsync(entity => entity.ID == id).ConfigureAwait(false);
}
I finally found where is the problem. GetAll with include uses generic TProperty instead of object to include the properties. Mock cant somehow relate the object Tproperty with the linq query i provide in test. Maybe there is a way to make it work but for now i simply changed the generic property to object

Entity Framework is Really Slow Getting a Single Item

Getting a single item from a table containing 6,000 records is taking about 30 seconds. Obviously, this is not acceptable and I can't figure out why. My stack is .NET 4.5, EF 6, and Web API 2. Is anything glaringly wrong with what I've done?
// DbSet
internal DbSet<TEntity> _dbSet;
// Ctor
public GenericRepository(TContext context)
{
_context = context;
_context.Configuration.ProxyCreationEnabled = false;
_dbSet = _context.Set<TEntity>();
}
// Really slow method
public TEntity GetByFilter(Func<TEntity,bool> filter, params Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = _dbSet;
if (includes != null)
{
foreach (var include in includes)
query = query.Include(include);
}
var entity = query.Where(filter).FirstOrDefault();
return entity;
}
// Here's how it's called. It returns a single item
var x = _unitOfWork.Repository.GetByFilter(i => i.WinId == id, null);
The reason why this is slow is you are using linq-to-objects in your Where clause, meaning you're executing the predicate on the client (C#) instead of the server (SQL), the C# is receiving 6000 database records and then filtering it in memory.
You can see this because your filter parameter is of type Func, which implies you're using linq-to-objects by way of the IEnumerable.Where extension.
Instead, what you want to use is the IQueryable.Where extension that takes a parameter of type Expression. This makes use of the Entity Framework query provider, and thus uses linq-to-ef instead.
Update your method signature to be as follows:
public TEntity GetByFilter(
Expression<Func<TEntity,bool>> filter,
params Expression<Func<TEntity, object>>[] includes)
This is illustrated further in the following stackoverflow answer https://stackoverflow.com/a/793584/507793

Using MOQ to verify expression parameter

I am trying to verify the method i am tested has been invoked with a particular expression. I have spent hours on this without the result i wanted.
This is the System under test
public class sut
{
private IEntityUtil _ew;
public sut(IEntityUtil ew)
{
_ew = ew;
}
public void Search()
{
Guid id = Guid.Parse("CB594050-3845-4EAF-ABC5-34840063E94F");
var res = _ew.SelectSingle<Post>(w => w.Id == id, new PersonalSiteEntities());
}
}
This is the dependency
public interface IEntityUtil
{
TEntity SelectSingle<TEntity>(Expression<Func<TEntity, bool>> predicate, System.Data.Objects.ObjectContext ctx)
where TEntity : EntityObject;
List<TEntity> SelectList<TEntity>(Expression<Func<TEntity, bool>> predicate, System.Data.Objects.ObjectContext ctx)
where TEntity : EntityObject;
bool Insert<TEntity>(TEntity entity, System.Data.Objects.ObjectContext ctx)
where TEntity : EntityObject;
}
And this is how i am trying to test it
public class tst
{
[TestMethod]
public void tst1()
{
var cwMock = new Mock<ConsoleApplication1.IEntityUtil>();
Guid id = Guid.Parse("CB594050-3845-4EAF-ABC5-34840063E94F");
//cwMock.Setup(x => x.SelectSingle<ConsoleApplication1.Post>(w => w.Id == id, It.IsAny<System.Data.Objects.ObjectContext>())).Returns(new ConsoleApplication1.Post()).Verifiable();
//cwMock.Setup(x => x.SelectSingle(It.IsAny<Expression<Func<ConsoleApplication1.Post, bool>>>(), It.IsAny<System.Data.Objects.ObjectContext>())).Returns(new ConsoleApplication1.Post()).Verifiable();
Expression<Func<ConsoleApplication1.Post, bool>> func = (param) => param.Id == id;
cwMock.Setup(x => x.SelectSingle<ConsoleApplication1.Post>(func, It.IsAny<System.Data.Objects.ObjectContext>())).Returns(new ConsoleApplication1.Post());
var sut = new ConsoleApplication1.sut(cwMock.Object);
sut.Search();
//cwMock.VerifyAll();
cwMock.Verify(x => x.SelectSingle(func, It.IsAny<System.Data.Objects.ObjectContext>()));
}
}
Please note the second commented setup will make the test pass but it wont let me verify that a specific expression has been passed in.
Thanks in advance.
Two things that will greatly help (us help you) are
Give your test a meaningful name so we know what you're after,
Arrange your test into three areas, and separate by comments or whitespace:
Arrange,
Act,
Assert
This makes it a bit more clear what the action is.
That said, it appears to me that you are trying to assert that ew.SelectSingle was called once when calling sut.Search()?
Also I noticed you're creating the Guid in your test but never doing anything with it. So, here is a quick proposal on your test (not using the IDE so you may find errors):
[Fact]
public void Verify_SelectSingle_IsCalledOnce( ){
Guid id = Guid.Parse( "CB594050-3845-4EAF-ABC5-34840063E94F" );
var cwMock = new Mock<ConsoleApplication1.IEntityUtil>( );
var post = new ConsoleApplication1.Post{ Id = id };
cwMock
.Setup( x=> x.SelectSingle<ConsoleApplication1.Post>(It.IsAny<Guid> ))
.Returns( post );
var sut = new ConsoleApplication1.sut(cwMock.Object);
sut.Search();
cwMock.Verify(
x=> x.SelectSingle( It.IsAny<ObjectContect>( o => o.Id == id )),
Times.Once);
}
As already noted, it's very hard to tell exactly what you're trying to accomplish because you haven't made your test names explicit. That said, it looks like you need to provide a callback to your mocked setup that will allow you to verify the expression. Something like this should help you (using the commented out setup that passes):
cwMock.Setup(x => x.SelectSingle(It.IsAny<Expression<Func<ConsoleApplication1.Post, bool>>>(), It.IsAny<System.Data.Objects.ObjectContext>())).Callback<Expression<Func<ConsoleApplication1.Post, bool>>>(VerifyExpression).Returns(new ConsoleApplication1.Post()).Verifiable();
Then create a callback method
private static void VerifyExpression(Expression<Func<ConsoleApplication1.Post, bool>> expression)
{
var func = expression.Compile();
// call func(params) and verify against it
}

Categories

Resources