Тестирование EF 4.0 с шаблонами POCO и t4 - как макетировать контекст? - PullRequest
7 голосов
/ 01 сентября 2010

Я пытаюсь создать поддельный контекст, соответствующий http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx

Как я вижу, есть интерфейс, который предоставляет методы, которые возвращают IObjectSet <...>, но шаблоны T4 генерируют методы, которые возвращают ObjectSet<...> и нет сгенерированного интерфейса, и на этой странице автор добавляет интерфейс к созданному контексту и дает ему возможность создавать макеты и т. д.

Моя главная цель - использовать шаблоны T4 для генерации классов poco исоздать фиктивный / поддельный контекст для проверки моих пользовательских репозиториев.Есть ли способ заставить его работать без написания или изменения шаблона T4 ??Как я могу создать макеты над контекстом (для IObjectSet это не тривиально), если он возвращает ObjectSet вместо IObjectSets ...

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

Ответы [ 2 ]

3 голосов
/ 01 сентября 2010

Автор просто издевается над хранилищем, а не над сущностями. EntityFramework генерирует ObjectQueries, но он оборачивает их, и его хранилище возвращает IObjectQueries. Он делает это, чтобы легко смоделировать данные, а затем во время сохранения он просто проверяет сущности.

Если вы просто пытаетесь создать «фиктивное» хранилище, вы можете создать свой собственный шаблон T4, выполнить итерации по файлу edmx и сгенерировать код. Но нет причин для генерации POCOS? Они уже существуют, зачем вам их воссоздавать? Он абстрагировал все в «общий» FakeObjectSet, так что на самом деле не так много кода для написания?

Вы пытаетесь сгенерировать это:

   public IObjectSet<Blog> Blogs
    {
        get
        {
            return _blogs ?? (_blogs = new FakeObjectSet<Blog>());
        }
        set
        {
            _blogs = value as FakeObjectSet<Blog>;
        }
    }
    private FakeObjectSet<Blog> _blogs;

Если так, я собираюсь догадаться, что вы проведете больше времени с T4, тогда вы просто напишите это.


Пример T4 без объявления класса ... вы можете сделать полный t4, выполнив этот блог

<#
    foreach (EntitySet set in container.BaseEntitySets.OfType<EntitySet>())
    {
#>
public IObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>
{
    get{
       return <#=code.FieldName(set)#> ??  ( <#=code.FieldName(set)#> = FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>("<#=set.Name#>"));
    }
    set{
   <#=code.FieldName(set)#>  = value as FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>("<#=set.Name#>");
    }
}
 private FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>> <#=code.FieldName(set)#>;
<#
 }

#>

Что бы сгенерировать этот код:

public IObjectSet<Blogs>{
  get{
     return _Blogs??  ( _Blogs = FakeObjectSet<Blog>("Blogs"));
   }
  set{
    _Blogs= value as FakeObjectSet<Class>("Blogs");
  }
}

private FakeObjectSet<Blog> _Blogs;

Примечание.

IObjectSet содержится в System.Data, поэтому добавьте ссылку на System.Data.Entity.dll

1 голос
/ 01 сентября 2010

Цитата в Искусство модульного тестирования Роя Ошерова:

Нет объектно-ориентированной проблемы, которая не может быть решена путем добавления слояот косвенности, за исключением, конечно, слишком большого количества слоев косвенности.

Ниже приведена моя смешная установка EF4 POCO.Я не использовал T4, так как было слишком сложно разобраться, как очистить шаблон, чтобы не генерировать слишком много gumpf.Вы, конечно, можете взломать шаблон T4, чтобы выдать что-то вроде этой структуры.

Хитрость заключалась в том, чтобы создать ObjectSet<T> вручную и выставить их как IQueryable.Поскольку Add и Create находятся на ObjectSet<T> / ObjectSet<T>, мне также пришлось добавить методы для добавления и создания сущностей.

public interface IStackTagzContext {
    IQueryable<Question> Questions { get; }

    Question CreateQuestion();

    void CreateQuestion(Question question);

    void SaveChanges();
}

public class StackTagzContext : ObjectContext, IStackTagzContext {   
    public StackTagzContext() : base("name=myEntities", "myEntities")  
    {
        base.ContextOptions.LazyLoadingEnabled = true;
        m_Questions = CreateObjectSet<Question>();
    }

    #region IStackTagzContext Members
    private ObjectSet<Question> m_Questions;
    public IQueryable<Question> Questions {
        get { return m_Questions; }
    }


    public Question CreateQuestion() {
        return m_Questions.CreateObject();
    }
    public void AddQuestion(Question question) {
        m_Questions.AddeObject(question);
    }

    public new void SaveChanges() {
        base.SaveChanges();
    }

    #endregion
}

Теперь вы заметите, что тип коллекции сущностей наинтерфейс IQueryable<T>, в отличие от IObjectSet<T>.Я не мог быть обеспокоен созданием FakeObjectSet и IQueryable предоставил мне достаточно гибкости.Итак, чтобы поцеловать, я обошелся без него.

Насмешка IQueryable, с другой стороны, тривиальна:

using Moq;
[TestClass]
public class TestClass {

  Mock<IStackTagzContext> m_EntitiesMock = new Mock<IStackTagzContext>();


  [TestMethod()]
  public void GetShouldFilterBySite() {
      QuestionsRepository target = new QuestionsRepository(m_EntitiesMock.Object);

      m_EntitiesMock.Setup(e=>e.Questions).Returns(new [] {
        new Question{Site = "site1", QuestionId = 1, Date = new DateTime(2010, 06,23)},
      }.AsQueryable());
  }
}
...