Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Include #15

Closed
starbugs2008 opened this issue Mar 4, 2020 · 7 comments
Closed

Support Include #15

starbugs2008 opened this issue Mar 4, 2020 · 7 comments

Comments

@starbugs2008
Copy link

I used ReturnsDbSet to mock the data. But it does not support include clause.
What is the syntax to support include?

@adrichema
Copy link

Would love to hear more about this, I also need this feature :)

@MichalJankowskii
Copy link
Owner

Are you able to help me with development? Last time I have limited time to support my all projects that is why this feature is parked a bit.

@MichalJankowskii
Copy link
Owner

Let's assume EF code like this:

public class Address
{
    public int Id { get; set; }
    public string Line1 { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Address Address { get; set; }
}

public class PersonContext : DbContext
{
    public virtual DbSet<Person> Persons { get; set; }
    public virtual DbSet<Address> Addresses { get; set; }
}

public class PersonsController
{
    private readonly PersonContext personContext;

    public PersonsController(PersonContext personContext)
    {
        this.personContext = personContext;
    }

    public IEnumerable<Person> GetPersons()
    {
        return this.personContext.Persons.Include("Address").ToList();
    }
}

Then Include mock should work like this:

[Fact]
public void TestWithInclude()
{
    var address = new Address {Id = 1, Line1 = "123 Main St."};
    var expectedPersons = new List<Person>
    {
        new Person {Id = 1, Address = address, Name = "John"},
        new Person {Id = 2, Address = address, Name = "John Jr."},
    };

    var personContextMock = new Mock<PersonContext>();
    personContextMock.Setup(m => m.Persons.Include("Address")).Returns(expectedPersons.AsQueryable());
    ...
}

Have you tried this solution?

@jerone
Copy link

jerone commented Mar 9, 2023

@MichalJankowskii When I run similar code, I get the following error:

cannot convert from 'System.Linq.IQueryable<Person>' to 'Microsoft.EntityFrameworkCore.Query.IIncludableQueryable<Person, Address>

But more important, how do I verify with above code that my SUT implementation actually contains an Include()?

Take for example above method GetPersons(), if I change that to the following, the test will still succeed:

    public IEnumerable<Person> GetPersons()
    {
-        return this.personContext.Persons.Include("Address").ToList();
+        return this.personContext.Persons.ToList();
    }

That's because the test expected db result already contains the "navigation" part (Address = address).

Similar issue: romantitov/MockQueryable#45

@MichalJankowskii
Copy link
Owner

This is a great question.

Provided example worked fine in 2020. From that time, Microsoft changed the implementation of EF library. The latest implementation uses extension methods for the Include function. And it is not possible to use Moq to fake it.

This is the limitation of EFCore architecture and Moq features. I am not able to support this in this lib.

You could create your own extensions and wrap those from EF. Here you could mock that based on your implementation. But this is not the approach I would like to have in the code.

@cervengoc
Copy link

Hi @MichalJankowskii ,

Sorry for re-opening this, but I felt like it's better than opening yet another issue for this.

First of all thank you for your efforts put into this little library, looks very promising.

While I understand that implementing the full proper Include functionality is difficoult to say the least, but I'd also like to at least validate the added includes in my unit tests.

I see that in the current EFCore there is not too much change to catch these calls, as the code checks whether the provider is an instance of EntityQueryProvider (see here). However, it would probably be a small bit of flexibility to modify this by introducing an interface like IIncludableQueryProvider (as a marker interface). I don't think it would have too much impact in any manner to EF Core. Then EntityQueryProvider could implement that, and you could implement that too, so you would receive the CreateQuery calls with the Include method. This way you could track the added includes in some way so at least the added includes would be assertable in a unit test. Does this make sense? Do you think it's worth a try to add a PR to EFCore to allow this extensibility?

Thank you

@cervengoc
Copy link

cervengoc commented Feb 1, 2024

As an addition, I see that this EntityQueryProvider in EF Core is part of the public interface, and also has all the methods as virtual. As a possible alternative approach (without needing any modifications in EF Core), could it be possible to inherit the mocked provider from this one and re-use the base test query provider in some other way inside?

Update: I've just realized that it's technically part of the public API but it's in the Internal namespace, and also noted that it should be used accordingly so perhaps it's not a reliable way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants