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
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);
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;
}
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
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
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
}