2013-11-15 4 views
17

Были поисковые запросы для решения проблемы о том, как издеваться над методом include на dbset в EF6. проблема хорошо документирована здесь: -Entity framework 6 mocking включает метод на dbset

http://entityframework.codeplex.com/discussions/461731

К сожалению, хотя, кажется, не самое лучшее решение там.

Кто-нибудь нашел обходное решение для этого?

Я действительно понимаю, что мы действительно не должны насмехаться над контекстом EF6, но руководство проектом настаивало на этом.

Заранее спасибо.

+0

, который насмешливый каркас? вы пробовали «фальшивые рамки»? – daryal

+0

Приношу свои извинения. Издевается над носорогами. –

+0

Подождите, вы издеваетесь над DbContext или DbSet? –

ответ

11

Итак, это возможно, если немного фальшивка!

В нижеследующем я настраиваю контекст макета и устанавливает и может звонить успешно. Я думаю, что секретный соус заключает вызовы через Provider, Expression и GetEnumerator и в настройке свойств DbSet на заштрихованном контексте на оштукатуренные множества, а не на укорачивание контекста для их возврата.

A runnable example is available on 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); 

    } 
+0

Кто-нибудь хочет добавить пример с Moq? У меня такая же проблема, как описано здесь. Https://entityframework.codeplex.com/discussions/461731 – GetFuzzy

+0

@GetFuzzy проект github, с которым я ссылаюсь, имеет тесты Moq.Я нахожусь на очень медленном интернет-подключении к Интернету прямо сейчас, поэтому не могу проверить, чтобы подтвердить, но я уверен, что они бегут. –

+0

Пол, спасибо за подсказку, я просмотрел проект github и да, они работают ... Тем не менее, я пробую это с EF6 и новым DbSet, не используя IDbSet, и хотя вы ожидали, что это будет быть похожим, или то же самое, мне не повезло. Я буду продолжать работать над ним, хотя, похоже, это должно быть выполнимо ... – GetFuzzy

22

Я была такая же драма, как @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); 

нибудь есть объяснение этому?

+9

'set.Setup (s => s.Include (It.IsAny ())). Возвращает (set.Object);' работает для меня. – Sam

+3

Я не был уверен, но 's.Include (It.IsAny ())' ** DOES ** работает при использовании лямбда. Престижность! – krillgar

+1

Я также добавлю следующую строку в конструктор 'FakeDbSet ', поэтому мне не нужно повторять это во всех тестах: 'Setup (m => m.Include (It.IsAny ())). Returns (Object); ' В настоящее время у меня есть много модульных тестов, в которых Moq успешно работает с этим подходом. – KarlZ

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:

Причина дополнительных битов мы можем проверить результат на тесте, если добавить или удалить, мы можем проверить переданную коллекцию и это будет иметь результат после теста.

+1

Это сработало для меня - оно позволяет избежать «NullReferenceException». То, что мне не хватало, было то, что я думал, что мне нужно переделать 'Include (Expresssion >)' overload - что вы не можете, потому что это метод расширения - когда на самом деле вам просто нужно высмеять core 'Include (string)', чтобы заставить все работать. – OffHeGoes

+0

Это не работает для меня. Когда я добавляю строку «mockList.Setup (m => m.Include (It.IsAny ())). Возвращает (mockList.Object);", "Include" получает красную squiggly с ошибкой "CS0411 \t type для метода 'EntityFrameworkQueryableExtensions.Include (IQueryable , выражение >)' не может быть выведено из использования. Попробуйте явно указать аргументы типа. " –

+0

@PaulGorbas Вам должно быть не хватает включить Я говорю из памяти, но я думаю, что это System.Data.Entity. –

Смежные вопросы