1

I have a method that modifies data in handler.

if (request.Active)
{
    var consentsToDeactivate = await _context.Consents.Where(x => x.Id != consent.Id).ToListAsync();
    consentsToDeactivate.ForEach(x => x.Active = false);
    _context.Consents.UpdateRange(consentsToDeactivate);
}

I need to mock this to to get a list where Consents are not equal to specific ID but I fail to do so. Here's what I've tried already:

var consentList = new List<Consent>()
{
    new Consent{ Id = new Guid("9205d00d-b443-412c-b8ad-6dcddc140ddf"), Name ="Consent1", Active=true, CreatedDate = DateTime.Now },
    new Consent{ Id = new Guid("6fa0ddb1-9867-49d5-b093-0f7eeb3fcbb6"),Name ="AConsent2", Active=false, CreatedDate = DateTime.Now },
}.AsQueryable();

var context = new Mock<IUnicornDbContext>();
var consentData = new Mock<DbSet<Consent>>();

consentData.Setup(x => x.FindAsync(It.IsAny<Guid>())).ReturnsAsync(new Consent() { Content = "some content", Name = "Test Consent", CreatedDate = DateTime.Now });
consentData.Setup(x => x.Where(It.IsAny<Expression<Func<Consent, bool>>>())).Returns(consentList);

context.Setup(x => x.Consents).Returns(consentData.Object);

But it fails to do so with exception below:

System.NotSupportedException: 'Unsupported expression: x => x.Where(It.IsAny<Expression<Func<Consent, bool>>>()) Extension methods (here: Queryable.Where) may not be used in setup / verification expressions.'

Been stuck on this for over 4 days with zero steps forward. I can't do anything with a code. Only able to write tests for the project. I know that expressions is complicated to mock but I'm looking forward to solve it.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Kittu
  • 13
  • 3
  • The answers so far have covered off the issue raised, in that you can't mock extensions. But what's the real problem... do you just need to arrive at a working dbcontext that supports async ops? What have you done so far in terms testing with a dbcontext? Are you trying to exclusively mock it, are you using the in-memory provider etc. – rgvlee Sep 22 '20 at 12:40
  • i already made a working test for simple context.Consents.ToListAsync(), and mocked a service provider which consist of multiple other services like SignInManager UserManager etc. And this project not using any repositories, business layer straight calling a context... Mocking Linq expression is the last thing i need to do. – Kittu Sep 22 '20 at 12:57
  • Usually anything using ToListAsync will need an async enumerable. Anyway, I guess the point I was going to arrive at is there are easier ways of testing with a DbContext, and from there the LINQ extensions become details you don't need to worry about, they just work as you'd expect. There are a few dbcontext mocking libraries around that support the async ops, https://github.com/rgvlee/EntityFrameworkCore.Testing which I maintain will do it. – rgvlee Sep 22 '20 at 13:24
  • Thank you, ill take a look and hope it will work – Kittu Sep 22 '20 at 13:33

2 Answers2

0

You can't mock extension methods. It's not actually part of the object, it's just a static method.

collection.Where(x => x.foo == 1) is actually Enumerable.Where(collection, x.foo). Mocks change the behavior of the object, but they can't control what happens in a static method it gets passed to. The only control you have is to see what the Enumerable.Where method calls on your object and mock those properties/methods.

But I think we ought to back up a bit. I think the road that lead you to wanting to mock the method Where is more than likely a wrong one.

What is this behavior that we're testing? It looks like when the request is active you should deactivate every consent except for the current one. It's probably important that the correct consents are deactivated, so the Where is important to test as well. If you mock it it makes your test a worse test. What I would recommend is having a realistic collection of consents in your Consent collection. Go find out how to populate that, I don't whether than means mocking your database code or using some in memory abstraction, but there will be advice online tailored to whatever you're using.

Nathan Cooper
  • 6,262
  • 4
  • 36
  • 75
  • Is it possible to mock a dbContext so whenever static method gets called it will filter a data and return the result? f.e im giving a Consents dbset of objects so when .Where() or .FindAsync() gets called i dont need to mock them and it will return a result? im new at mocking, and this is the only problem i met so far and been stuck. So just thinking of possible ways how to get around mocking extension methods. – Kittu Sep 22 '20 at 11:09
  • It sounds like you're using Entity Framework? There's a good example of someone using a inmemory database here: https://stackoverflow.com/a/54220067/1734730. I'm going to guess EF Core. You could do something similar to put the data you need into your Consent collection. That's what I'd do. – Nathan Cooper Sep 22 '20 at 19:21
-1

You will not be able to mock the static method.

Why don't you wrap the logic in a separate method and mock that instead.

var consentsToDeactivate = await Wrapper_Function(consent.Id); 

//....

public async Task<IQueryable<Consents>> Wrapper_Function(int consentId ){
   return await _context.Consents.Where(x => x.Id != consentId).ToListAsync();
}
CodeNepal
  • 732
  • 7
  • 11