Entity framework 6 mocking включает метод на dbset
Были ли поисковые запросы для решения проблемы того, как издеваться над методом include на dbset в EF6.
Проблема хорошо документирована здесь: -
http://entityframework.codeplex.com/discussions/461731
К сожалению, хотя там, похоже, не существует верного решения.
Кто-нибудь нашел обходное решение для этого?
Я понимаю, что мы действительно не должны издеваться над контекстом EF6, но руководство проекта настаивало на этом.
Спасибо заранее.
Ответы
Ответ 1
Итак, это возможно, если немного faff!
В приведенном ниже разделе я устанавливаю макет контекста, а также устанавливает и может включать функцию include успешно. Я думаю, что секретный соус завершает вызовы провайдеру, выражению и GetEnumerator, а также устанавливает свойства DbSet на заштрихованном контексте на заштрихованные наборы и не прерывает контекст для их возврата.
Простой пример доступен в GitHub
[Test]
public void CanUseIncludeWithMocks()
{
var child = new Child();
var parent = new Parent();
parent.Children.Add(child);
var parents = new List<Parent>
{
parent
}.AsQueryable();
var children = new List<Child>
{
child
}.AsQueryable();
var mockContext = MockRepository.GenerateStub<TestContext>();
var mockParentSet = MockRepository.GenerateStub<IDbSet<Parent>>();
var mockChildSet = MockRepository.GenerateStub<IDbSet<Child>>();
mockParentSet.Stub(m => m.Provider).Return(parents.Provider);
mockParentSet.Stub(m => m.Expression).Return(parents.Expression);
mockParentSet.Stub(m => m.GetEnumerator()).Return(parents.GetEnumerator());
mockChildSet.Stub(m => m.Provider).Return(children.Provider);
mockChildSet.Stub(m => m.Expression).Return(children.Expression);
mockChildSet.Stub(m => m.GetEnumerator()).Return(children.GetEnumerator());
mockContext.Parents = mockParentSet;
mockContext.Children = mockChildSet;
mockContext.Parents.Should().HaveCount(1);
mockContext.Children.Should().HaveCount(1);
mockContext.Parents.First().Children.FirstOrDefault().Should().NotBeNull();
var query = mockContext.Parents.Include(p=>p.Children).Select(pc => pc);
query.Should().NotBeNull().And.HaveCount(1);
query.First().Children.Should().NotBeEmpty().And.HaveCount(1);
}
Ответ 2
У меня была та же драма, что и @GetFuzzy выше - казалось, что независимо от того, что я сделал, я не мог избежать исключения NullReferenceException всякий раз, когда был вызван вызов Include() на Moq DbSet. Пример Github в другом ответе, к сожалению, не сработал: Set.Include() всегда возвращает null.
После долгой игры я придумал обходной путь для этого.
[Test]
public void CanUseIncludeWithMocks()
{
var child = new Child();
var parent = new Parent();
parent.Children.Add(child);
var parents = new List<Parent> { parent };
var children = new List<Child> { child };
var parentsDbSet1 = new FakeDbSet<Parent>();
parentsDbSet1.SetData(parents);
var parentsDbSet2 = new FakeDbSet<Parent>();
parentsDbSet2.SetData(parents);
parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet2.Object);
// Can now test a method that does something like: context.Set<Parent>().Include("Children") etc
}
public class FakeDbSet<T> : Mock<DbSet<T>> where T : class
{
public void SetData(IEnumerable<T> data)
{
var mockDataQueryable = data.AsQueryable();
As<IQueryable<T>>().Setup(x => x.Provider).Returns(mockDataQueryable.Provider);
As<IQueryable<T>>().Setup(x => x.Expression).Returns(mockDataQueryable.Expression);
As<IQueryable<T>>().Setup(x => x.ElementType).Returns(mockDataQueryable.ElementType);
As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(mockDataQueryable.GetEnumerator());
}
}
Мне действительно не нравится неуклюжесть необходимости иметь два поддельных DbSets, но по какой-то причине это не работает:
parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet1.Object);
У кого есть объяснение?
Ответ 3
с использованием фреймворка Moq этот метод работает со всем, что я бросаю на него.
public static Mock<DbSet<T>> GetMockSet<T>(this ObservableCollection<T> list) where T : class
{
var queryable = list.AsQueryable();
var mockList = new Mock<DbSet<T>>(MockBehavior.Loose);
mockList.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockList.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockList.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockList.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
mockList.Setup(m => m.Include(It.IsAny<string>())).Returns(mockList.Object);
mockList.Setup(m => m.Local).Returns(list);
mockList.Setup(m => m.Add(It.IsAny<T>())).Returns((T a) => { list.Add(a); return a; });
mockList.Setup(m => m.AddRange(It.IsAny<IEnumerable<T>>())).Returns((IEnumerable<T> a) => { foreach (var item in a.ToArray()) list.Add(item); return a; });
mockList.Setup(m => m.Remove(It.IsAny<T>())).Returns((T a) => { list.Remove(a); return a; });
mockList.Setup(m => m.RemoveRange(It.IsAny<IEnumerable<T>>())).Returns((IEnumerable<T> a) => { foreach (var item in a.ToArray()) list.Remove(item); return a; });
return mockList;
}
использовать его просто:
mockContext.Setup(p => p.<DbSetToMock>).Returns(<observableCollection to use as data>.GetMockSet().Object);`
Это работает, если контекст реализует интерфейс, поскольку вам никогда не нужно что-либо делать с EF.
EDIT:
Причиной для дополнительных бит является проверка результата теста, если мы добавим или удалим, мы можем проверить пройденную коллекцию и получить результат после теста.