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

Multiple calls to getter (that returns an IEnumerable) returns null 2nd time #618

Closed
TheDarkTrumpet opened this issue Apr 25, 2018 · 3 comments

Comments

@TheDarkTrumpet
Copy link

Hi All,

I ran into an odd issue that may be part my fault, but this seems odd to me, so figured I'd ask about it vs working around it. I'm using Moq 4.8.2 for this test

Basically, I'm testing a class that deals with EntityFramework. The class has a method to return an IEnumerable, which I setup through a Mock DbSet object. Calling it once works, calling it a second time returns 0 elements. Using SetupSequence also doesn't help here. I'm not sure if this is a bug or I'm just missing something with this all.

Below is a linqpad script (relies on Moq and EntityFramework) that describes what I'm seeing:

IQueryable<ClassA> myClassAs = new List<ClassA>() {
	new ClassA() { Id = 1, Name = "MyClass #1" },
	new ClassA() { Id = 2, Name = "MyClass #2" },
	new ClassA() { Id = 3, Name = "MyClass #3" }
}.AsQueryable();

Mock<DbSet<ClassA>> mockSet = new Mock<DbSet<ClassA>>();
mockSet.As<IQueryable<ClassA>>().Setup(m => m.Provider).Returns(myClassAs.Provider);
mockSet.As<IQueryable<ClassA>>().Setup(m => m.Expression).Returns(myClassAs.Expression);
mockSet.As<IQueryable<ClassA>>().Setup(m => m.ElementType).Returns(myClassAs.ElementType);
mockSet.As<IQueryable<ClassA>>().Setup(m => m.GetEnumerator())
	.Returns(myClassAs.GetEnumerator());
	
Mock<dbContext> context = new Mock<dbContext>();	
context.Setup(x => x.ClassAs).Returns(mockSet.Object);

RealClass testClass = new RealClass(context.Object);

Console.WriteLine("Using Setup");
Console.WriteLine("Before Invocation, total #: " + myClassAs.Count());
List<ClassA> results1 = context.Object.ClassAs.ToList();
Console.WriteLine("First Invocation, total #: " + results1.Count());
List<ClassA> results2 = context.Object.ClassAs.ToList();
Console.WriteLine("Second Invocation, total #: " + results2.Count());

Console.Write("\n\n=======\n\n");

context.SetupSequence(x => x.ClassAs).Returns(mockSet.Object);
Console.WriteLine("Using SetupSequence (Singular)");
Console.WriteLine("Before Invocation, total #: " + myClassAs.Count());
results1 = context.Object.ClassAs.ToList();
Console.WriteLine("First Invocation, total #: " + results1.Count());
results2 = context.Object.ClassAs?.ToList();
Console.WriteLine("Second Invocation, total #: " + results2?.Count());

Console.Write("\n\n=======\n\n");

context.SetupSequence(x => x.ClassAs)
	.Returns(mockSet.Object)
	.Returns(mockSet.Object);
Console.WriteLine("Using SetupSequence (Multiple)");
Console.WriteLine("Before Invocation, total #: " + myClassAs.Count());
results1 = context.Object.ClassAs.ToList();
Console.WriteLine("First Invocation, total #: " + results1.Count());
results2 = context.Object.ClassAs?.ToList();
Console.WriteLine("Second Invocation, total #: " + results2?.Count());
}

public class ClassA
{
	public int Id {get;set;}
	public string Name {get;set;}
}

public class dbContext : DbContext
{
	public virtual DbSet<ClassA> ClassAs { get; set; }
}

public class RealClass
{
	private readonly dbContext _context;
	
	public RealClass(dbContext context)
	{
		_context = context;
	}
	public virtual IEnumerable<ClassA> ClassAs => _context.ClassAs;

The output of this script is the following:

Using Setup
Before Invocation, total #: 3
First Invocation, total #: 3
Second Invocation, total #: 0


=======

Using SetupSequence (Singular)
Before Invocation, total #: 3
First Invocation, total #: 0
Second Invocation, total #: 


=======

Using SetupSequence (Multiple)
Before Invocation, total #: 3
First Invocation, total #: 0
Second Invocation, total #: 0
@stakx
Copy link
Contributor

stakx commented Apr 25, 2018

Without having done any in-depth analysis, this seems wrong to me:

mockSet.As<IQueryable<ClassA>>().Setup(m => m.GetEnumerator())
                                .Returns(myClassAs.GetEnumerator());

Shouldn't you use lazy evaluation to always return a fresh enumerator?

mockSet.As<IQueryable<ClassA>>().Setup(m => m.GetEnumerator())
                                .Returns(() => myClassAs.GetEnumerator());
//                                       ^^^^^

(Btw. it might be necessary to set up both IEnumerable<T>.GetEnumerator and IEnumerable.GetEnumerator.)

@TheDarkTrumpet
Copy link
Author

Interesting, I changed the line you recommended, and that did indeed solve it. I really appreciate the quick reply, and I think I need to read up on what you mean by "lazy evaluation" in this case - or more accurately what () => ... actually does. The only time I've really used that is when referencing a variable in the expression after the arrow.

Thanks again for the comment, this gives me something to research and try to understand better.

@TheDarkTrumpet
Copy link
Author

One other comment for an others who read this. Resharper recommended this line instead, and it does work as well:

mockSet.As<IQueryable<ClassA>>().Setup(m => m.GetEnumerator())
	.Returns(myClassAs.GetEnumerator);

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

2 participants