Is this a bad Moq setup or a deficiency in Moq? - c#

I am attempting to test the following code:
public async Task<Activity> Get(long ID, Recruiter User, bool IsArchived = false)
{
Activity result = await collection.FirstOrDefault(x => x.ID == ID && x.Recruiter.CompanyID == User.CompanyID && (!x.Archived || IsArchived));
return result;
}
With the following test:
[TestMethod]
public async Task GetDoesThings()
{
long ID = 1;
bool IsArchived = false;
Recruiter User = new Recruiter()
{
CompanyID = 1
};
ActivitiesMock.Setup(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived))).ReturnsAsync(new Activity());
Activity result = await repo.Get(ID, User);
ActivitiesMock.Verify(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)));
}
(I know there are different ways of writing it, this is the most recent iteration we've tried.)
The ActivitiesMock relates to the collection found in Get(long ID, Recruiter User, bool IsArchived = false). We recently wrote wrappers in order to try and test our Entity calls more efficiently, but we're running into this error when trying to verify the calls are made correctly:
Test method ExampleProject.Tests.Backend.Repositories.ActivityRepositoryTests.GetDoesThings threw exception:
Moq.MockException:
Expected invocation on the mock at least once, but was never performed: x => x.FirstOrDefault(y => (y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID) && (!(y.Archived) || .IsArchived))
Configured setups:
x => x.FirstOrDefault(y => (y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID) && (!(y.Archived) || .IsArchived)), Times.Never
Performed invocations:
IAppCollection`2.FirstOrDefault(x => (((x.ID == value(ExampleProject.Backend.Repositories.ActivityRepository+<>c__DisplayClass2_0).ID) AndAlso (x.Recruiter.CompanyID == value(ExampleProject.Backend.Repositories.ActivityRepository+<>c__DisplayClass2_0).User.CompanyID)) AndAlso (Not(x.Archived) OrElse value(ExampleProject.Backend.Repositories.ActivityRepository+<>c__DisplayClass2_0).IsArchived)))
In this instance the wrapper (collection) is a mock of the interface. The goal is to ensure that the repository is calling the correct expression on the wrapper, so that we know the predicate that's being passed to the Entity DbSet is correct without having to worry about all the messy async abstractions.
The Mock Setup() with the full predicate doesn't appear to be found when the test is ran, and when I change the Setup() to It.IsAny<Expression<Func<Activity, bool>>>() it runs the mock and provides the return, but the Verify call doesn't work. So, running:
ActivitiesMock.Setup(x => x.FirstOrDefault(It.IsAny<Expression<Func<Activity, bool>>>())).ReturnsAsync(new Activity());
Activity result = await repo.Get(ID, User);
Assert.IsNotNull(result);
ActivitiesMock.Verify(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)));
Passes the assert but fails the Verify, whereas running:
ActivitiesMock
.Setup(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)))
.ReturnsAsync(new Activity())
.Verifiable();
Activity result = await repo.Get(ID, User);
Assert.IsNotNull(result);
ActivitiesMock.Verify();
Fails on the assert.
It looks like it's failing because it's expecting the same object types. Am I trying to do something that Moq can't handle, or am I missing something that I need to do in order to get the verification to be correct?
By request, the concrete implementation of the LINQ-to-Entity wrapper (collection) is:
public Task<T> FirstOrDefault(Expression<Func<T, bool>> Predicate)
{
return DbSet.FirstOrDefaultAsync(Predicate);
}
Though the wrapper itself isn't being used, but an interface for it is being mocked, and it's that mock that we're testing.

The answer is neither. Since the anonymous functions have to create class instances to store the data being provided, they create DisplayClass instances to hold the data. Since those instances are being created in different namespaces (among other things) they don't pass when Moq calls .Equals against them.
We solved this problem by writing our tests as such:
ActivitiesMock
.Setup(x => x.Where(It.IsAny<Expression<Func<Activity, bool>>>()))
.Returns((Expression<Func<Activity, bool>> x) =>
{
actualPredicate = x;
return queryMock.Object;
});
Then creating valid and invalid activities to provide to the predicate to ensure it returns true or false correctly:
Assert.IsTrue(actualPredicate.Compile().Invoke(validActivity));
Granted for now it's a bit hacky, but at first glance it doesn't seem too much like a dumpster fire solution, and it's a way for us to ensure the calls being provided do what we expect them to, which is what we want.
Update (Sep 7th, 2016): So far this has been working well for us. We've ran into an issue where LINQ-to-Entity statements don't run as expected because the LINQ is case-sensitive and the SQL generated isn't, but since that isn't a deal breaker for us we're just fine.

Related

C# Using Equals in Where clause doesn't translate to proper query

Why asking
I already found a way to fix it, but it duplicates the code in Equals method and moreover I would like to understand the behavior.
Issue
Querying DbContext list of objects and filtering using a where clause that uses the Equals method of the object doesn't return the proper SQL query.
So, here is my Equals method:
public override bool Equals(object obj)
{
if (obj == null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj is not MetaMovie other) return false;
if (other.Id > 0 && Id > 0) return other.Id == Id;
return other.MetaSource == MetaSource && other.ExternalId == ExternalId;
}
Using:
var existingMovie = await db.MetaMovies.Where(m => m.Equals(movie)).FirstOrDefaultAsync();
Generate a where using Id even if movie.Id == 0 and I was expecting having a query that would have used the alternate key (ExternalId + MetaSource). But no!
Patch that would prefer to skip
Instead, I created this method in the MetaMovie class:
public static System.Linq.Expressions.Expression<Func<MetaMovie, bool>> IsEqual(MetaMovie other)
{
if (other.Id > 0) return m => other.Id == m.Id;
return m => other.MetaSource == m.MetaSource && other.ExternalId == m.ExternalId;
}
And now it returns the proper query depending if movie.Id == 0 or not using:
var existingMovie = await db.MetaMovies.Where(MetaMovie.IsEqual(movie)).FirstOrDefaultAsync();
Could someone tell me what is difference? Or even better, how could I only have the Equals method having the same behavior?
Can you try movie.Equals(m))
instead? Without seeing the rest of the code, it's a little tough.
if (obj is not MetaMovie other) return false;
could also be the issue... But as I said it's a little tough to figure it out without seeing the rest.
I think the difference is you are sending in an arbitrary Equals method and EF does not know how to interpret that. The one that works is a LINQ expression body and EF can understand those and is able to create the proper SQL for it.
You can also simply inline the lambda expression.
await db.MetaMovies.Where(m =>
movie.Id > 0
? movie.Id == m.Id
: movie.MetaSource == m.MetaSource && movie.ExternalId == m.ExternalId)
EF Core can only translate LambdaExpressions, what you have is a compiled function.
You could write a helper method to apply the where expression to a query.
public static IQueryable<MetaMovies> WhereEqual(this IQueryable<MetaMovies> query, MetaMovie movie)
=> movie.Id > 0
? query.Where(m => movie.Id == m.Id)
: query.Where(m => movie.MetaSource == m.MetaSource && movie.ExternalId == m.ExternalId);
await db.MetaMovies.WhereEqual(movie).FirstOrDefaultAsync();
At least then, this common query is defined in a single location.

How to make reusable conditions that can be used in queryables? [duplicate]

This question already has answers here:
How to reuse where clauses in Linq To Sql queries
(4 answers)
Closed 1 year ago.
So in a function I've a large queryable and I apply a bunch of where cause on it based on other conditions.
Like in this example:
query.Where(i =>
_context.FicDernierEvt
.Where(y => y.VteAffaire == null && y.ApvAffaire == null)
.Select(y => y.IdFicheCrm)
.Contains(i.Id)
);
I've this condition _context.FicDernierEvt.Where(y => y.VteAffaire == null && y.ApvAffaire == null).Select(y => y.IdFicheCrm).Contains(i.Id) that is used a lot in my code.
I would like to avoid having this all accross my code so i've tried to make a function:
private bool isProspect(FicFicheCrm ficheCrm){
return _context.FicDernierEvt
.Where(y => y.VteAffaire == null && y.ApvAffaire == null)
.Select(y => y.IdFicheCrm)
.Contains(ficheCrm.Id);
}
So i could use it this way:
query.Where(i => isProspect(i));
But it didn't worked since, it's just not mean to be done that way.
Do someone have an idea on how to make reusable conditions like this to be used in queryables ?
My advice would be to extend LINQ with a Where method that contains your predicate.
If you are not familiar with Extension methods, consider to read Extension Methods Demystified
You fogrot to tell us what type of IQueryable<...> you store in _context.FicDernierEvt, but let's assume it is IQueryable<FicDernierEvt>. In other words, assume that _context.FicDernierEvt is a table of FicDernierEvts.
Requirement Procedure GetIdFics (TODO: invent proper name) takes as input an IQueryable<FicDernierEvt>, keeps only those ficDernierEvts that have a null value for both properties VteAffaire and ApvAffaire, and returns from every remaining ficDernierEvt the value of property ficDernierEvt.IdFic
I don't know the type of IdFic, but let's assume it is an int
public static IQueryable<int> GetIdFics( // TODO: invent proper name
this IQueryable<FicDernierEvt> source)
{
return source.Where(ficDernierEvt => ficDernierEvt.VteAffaire == null
&& ficDernierEvt.ApvAffaire == null)
.Select(ficDernierEvt => ficDernierEvt.IdFic);
}
That's all!
Usage:
IQueryable<int> myIdFics = _context.FicDernierEvt.GetIdFics();
You say you have this Where/Select in a lot of places:
var oldIdFics = _context.FicDernierEvt
.Where(ficDernierEvt.Date.Year < 2010)
.GetIdfics();
var invalidIdFics = _context.FicDernierEvt.GetIdFics()
.Where(idFic => idFic <= 0);
You can even use it in a more complicate LINQ statement:
IQueryable<FicDernierEvt> unpaidFicDernierEvts = this.GetUnpaidFicDernierEvts();
int customerId = this.GetCustomerId(customerName);
var largestUnpaidCustomerIdFic = unpaidFicDernierEvts
.Where(unpaidEvt => unpaidEvt.CustomerId == customerId)
.GetIdFics()
.Max();

Mock a call to IQueryable using moq

I'm trying to moq a repository I have which is defined as:
public IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null, bool disableTracking = true)
Is there a way to mock this? I want the query to execute, using the mock data i supply the repos. I'm not sure how to tell MOQ that when I call GetAll I want it to still run the query that is passed in, but do it aganist the dataset I supply. so it's not going to the db but against the mock set which I've configured. I'm able to create the data, it contain 12 records, but I want the getall moq call to execute it's query and filter it to just the 2 that should be returned.
The actual service where the call happens is:
var list = await _unitOfWork.GetRepository<CASE_ACTIVITY>().GetAll(predicate: x => x.SM_SITE_ID == siteId && x.CMS_USER_ID == userId
&& x.IS_DELETED == "N" && x.Appointment.IS_DELETED == "N" && x.Appointment.IS_ARCHIVED == "N" && x.IS_ARCHIVED == "N"
&& ((x.Appointment.APPOINTMENT_DATETIME.HasValue && x.Appointment.APPOINTMENT_DATETIME.Value.Date == DateTime.Today.Date)
|| (!x.Appointment.APPOINTMENT_DATETIME.HasValue && x.ACTIVITY_STATUS_ID == _appSettings.CASE_ACTIVITY_STATUS_ID_PENDING)))
.Include(x => x.Activity_Lookup).Include(x => x.Appointment).ThenInclude(x => x.Cms_Client).Include(x => x.Cms_Case)
.ToListAsync();
Assuming you have enough control over the GetRepository() method to make it return your mock repository, mocking the method itself is fairly straightforward (if a little verbose). I just dumped the GetAll method into an interface called IRepository, and this is what the mock looks like. Inside of the Returns method, you have access to each of the parameters to execute or ignore as you please.
var mock = new Moq.Mock<IRepository>();
mock.Setup(a => a.GetAll<int>(It.IsAny<Expression<Func<int, bool>>>(), It.IsAny<Func<IQueryable<int>, IOrderedQueryable<int>>>(), It.IsAny<Func<IQueryable<int>, IIncludableQueryable<int, object>>>(), It.IsAny<bool>()))
.Returns<Expression<Func<int, bool>>, Func<IQueryable<int>, IOrderedQueryable<int>>, Func<IQueryable<int>, IIncludableQueryable<int, object>>, bool>((param1, param2, param3, param4) =>
{
return new[] { 1, 2, 3 }.AsQueryable();
});
var result = mock.Object.GetAll<int>();
From here, we can't help much without seeing more code. In abstract terms, though, you'll take your mock.Object (which is of type IRepository) and feed it to whatever collection GetRepository() draws from. Also note, of course, that I used an int for the generic parameter -- you'll replace that with whatever type you're using. It may be possible to make a mock that accepts generic parameters, but that hopefully won't be necessary!

Entity Framework error using DefaultIfEmpty()

I have a problem with Entity Framework using the DefaultIfEmpty method. The following query is returning empty when it should return an offer that matches all criteria in the database.
If I remove one or both DefaultIfEmpty method calls it works, but with them it doesn't. I need those to prevend another problem in the query.
When I execute the generated SQL query directly on the database it works and it returns the offer.
I also made an Unit Test reproducing the same example and it also passes so it must be an Entity Framework issue.
Here's the query:
private static Expression<Func<Offer, bool>> AddFilter(Service criteria)
{
return offer => offer.Restrictions.
SelectMany(rest => rest.OperatorRange.DefaultIfEmpty(), (rest, alop) => new { Restriction = rest, OperatorRange = alop.Id }).
Where(alop => criteria.ServiceUseNet == null || alop.OperatorRange.ToUpper() == criteria.ServiceUseNet.ToUpper()).
SelectMany(rest => rest.Restriction.CallType.DefaultIfEmpty(), (rest, till) => new { Restriction = rest, CallType = till.Id }).
Any(till => criteria.UseServiceCoverage == null || till.CallType.ToUpper() == criteria.UseServiceCoverage.ToUpper());
}
Change it into two Any calls:
return offer => offer.Restrictions
.Any(rest
=> rest.OperatorRange
.Where(alop => criteria.ServiceUseNet == null
|| alop.OperatorRange.ToUpper() == criteria.ServiceUseNet.ToUpper())
.Any(till => criteria.UseServiceCoverage == null
|| till.CallType.ToUpper() == criteria.UseServiceCoverage.ToUpper()));
The predicate is supposed to test whether there are any OperatorRanges (meeting some criteria) having any CallTypes meeting some criteria. If there are no OperatorRanges, there won't be any CallTypes either, let alone matching CallTypes.
In this form, the predicate always returns true or false.

Test for List<T> membership using a List<T>

Does anyone know if there is a way to test for list membership utilizing a list. For example I have a class named Membership which has a property Rebates which is of type List<Enums.RebateType>. I want to test using a lambda expression to see if that list contains any rebates that are of a specific type. My orginal lambda expression is as follows
return Membership.Rebates.Exists(rebate =>
rebate.RebateType == Enums.RebateType.A &&
rebate.RebateStatus == Enums.RebateStatus.Approved);
Instead of having to do the following:
return Membership.Rebates.Exists(rebate =>
(rebate.RebateType == Enums.RebateType.A &&
rebate.RebateStatus == Enums.RebateStatus.Approved) ||
(rebate.RebateType == Enums.RebateType.B &&
rebate.RebateStatus == Enums.RebateStatus.Approved));
I was wondering if something similar to the following mocked up SQL syntax could be done via one Lambda expression.
SELECT COUNT(*)
FROM Membership.Rebates
WHERE RebateType IN (ValidRebateTypes) AND Approved = true
ValidRebateTypes is curently a List<Enums.RebateType> that I am testing for i.e. ValidRebateTypes = (Enums.RebateType.A, Enums.RebateType.B).
The work around I currently have is as follows:
bool exists = false;
foreach (Enums.RebateType rebateType in ValidRebateTypes())
{
exists = Membership.Rebates.Exists(
rebate =>
rebate.RebateType == rebateType &&
rebate.RebateStatus == Enums.RebateStatus.Approved);
if (exists) { break; }
}
return exists;
Sounds like you want:
Membership.Rebates.Where(r => ValidRebateTypes.Contains(r.RebateType)
&& r.RebateStatus == Enums.RebateStatus.Approved);
You can then use .Count() for the count:
Membership.Rebates.Where(r => ValidRebateTypes.Contains(r.RebateType)
&& r.RebateStatus == Enums.RebateStatus.Approved)
.Count();
Or .Any() to determine the existence of any that satisfy that condition
Membership.Rebates.Any(r => ValidRebateTypes.Contains(r.RebateType)
&& r.RebateStatus == Enums.RebateStatus.Approved);
In addition to Marc's suggestion, I would recomment making ValidRebateTypes a HashSet<Enums.RebateType>. Not only is this likely to be more efficient (although possibly not for a small set), it also reveals your intent (ie. that there only be one of each value of RebateType in it).

Categories

Resources