DbSet mock, никаких результатов при вызове ToList во-вторых
Я пытаюсь высмеять DbContext и DbSet. Это работает для моих предыдущих модульных тестов, но проблема возникает, когда мой код дважды вызывал метод ToList на DbSet.
Вначале dbSet.ToList() возвращает посмеянные результаты.
Второй возвращает 0 элементов;
var queryableData = new List<string>{ "a", "b", "c" }.AsQueryable();
var mockDbSet = new Mock<DbSet<string>>();
var q = mockDbSet.As<IQueryable<string>>();
q.Setup(m => m.Provider).Returns(queryableData.Provider);
q.Setup(m => m.Expression).Returns(queryableData.Expression);
q.Setup(m => m.ElementType).Returns(queryableData.ElementType);
q.Setup(m => m.GetEnumerator()).Returns(queryableData.GetEnumerator());
DbSet<string> dbset = mockDbSet.Object;
IQueryable<string> query = dbset;
//RESULTS: abc
var a1 = dbset.ToList();
foreach (var a in a1)
Console.Write(a);
//NO RESULTS
var a2 = dbset.ToList();
foreach (var a in a2)
Console.Write(a);
Ответы
Ответ 1
Вы возвращаете тот же самый экземпляр перечисления при каждом вызове GetEnumerator
. Когда он перечисляется один раз, это делается, EF не вызывает свой метод Reset
, а запрашивает новый счетчик.
Но вы возвращаете тот, который только что дал все элементы и больше не дает.
Вместо этого верните функцию, которая возвращает перечислитель, который будет возвращать новый счетчик каждый раз, когда вы его попросите.
q.Setup(m => m.GetEnumerator()).Returns( () => queryableData.GetEnumerator() );
Ответ 2
Я просто хотел добавить в Wiktor Zychla ответ на мою небольшую часть. Если кто-то ищет версию этого макета Async (из этого урока: http://msdn.microsoft.com/en-us/data/dn314429.aspx#async), то это моя модификация класса TestDbAsyncEnumerator<T>
:
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public TestDbAsyncEnumerator(Func<IEnumerator<T>> valueFunction)
{
_inner = valueFunction();
}
public void Dispose()
{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
public T Current
{
get { return _inner.Current; }
}
object IDbAsyncEnumerator.Current
{
get { return Current; }
}
}
Затем, как и Wiktor, вам нужно настроить его с помощью делегата, поэтому в случае async это будет так:
mockSet.As<IDbAsyncEnumerable<Blog>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<Blog>(() => data.GetEnumerator()));
Если кто-то хочет источник для этого, то здесь вы идете: https://github.com/kelostrada/EntityFrameworkWithMock.Test
Ответ 3
Я не могу комментировать запись в Wiktor, поскольку у меня недостаточно репутации, но я хотел бы добавить немного дополнительного ответа Wiktor.
Код
mockSet.Setup((m => m.GetEnumerator()).Returns(() => data.GetEnumerator())
Не получится, если вы дадите ему нулевой аргумент лямбда, как в примере (по крайней мере, для версии, которую я использую).
Передача аргумента, который никогда не используется, позволит ему выполнить требование подписи.
mockSet.Setup((m => m.GetEnumerator()).Returns(x => data.GetEnumerator())
Будет компилироваться и работает, как ожидалось.
И это также относится и к ответу Келу (хотя он добавил лямбда в неправильном месте.
mockSet.As<IDbAsyncEnumerable<Blog>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(x => new TestDbAsyncEnumerator<Blog>(data.GetEnumerator()));
Не пытаясь найти ответы здесь, я просто добавляю, потому что сам сделал эти ошибки:)
Ответ 4
Если вы положили предложение "Where" перед вызовом .ToList(), данные должны оставаться в наличии.
var a1 = dbset.Where(m => m != null).ToList();
var a2 = dbset.Where(m => m != null).ToList();