views:

168

answers:

2

I'm trying to create fake context accodring to http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx

As i can see there is an interface which exposes methods which returns IObjectSet<...>, but T4 templates generates methods which returns ObjectSet<...> and there is no generated interface and on that page author adds interface to created context and it gives him way to create mock etc.

My main goal is to use T4 templates to generate poco classes and create mock/fake context to test my custom repositories. Is there any way to make it works without writing or changing T4 template?? How can i create mocks above context (for IObjectSet is't trivial) if it's returning ObjectSet's instead of IObjectSets...

Thx in advance

+3  A: 

The author is just mocking the repository, not the entities. EntityFramework generate ObjectQueries, but he wraps them and his repository returns IObjectQueries. He does this so he can easily mock the data, then during the save he just validates the entities.

If you are just trying to create a "mock" repository you can create your own T4 template and iterate over the edmx file and generate the code. But there is no reason to have to generate POCOS? They already exist, why do you need to recreate them? He abstracted everything into a "generic" FakeObjectSet so there really isn't that much code to write?

Are you trying to generate this:

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

If so I am going to guess you are going to spend more time with T4 then you would just writing it.


Example T4 without the class declaration... you can do the complete t4 by following this blog

<#
    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)#>;
<#
 }

#>

Which would generate this code:

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

private FakeObjectSet<Blog> _Blogs;

Side note.

IObjectSet is contained in System.Data so Add a reference to System.Data.Entity.dll

Nix
Mayby I was miss understood, I'm just basing on this article. I know that author has POCOS classes, but i have large, already existing DB, i had to create edmx from this DB and next after many difficoulties (this is Oracle DB), i was able to use T4 templates to generate POCO classes, because i didn't write them before by hand. Now i wanted to add Unit Tests and i have to mock my context object if i want to test repositories. So T4 generated POCO classes for entities and class for Context, but there was missing interface for this context so i can't create mock based on this interface.
Simon
Thanks tohttp://slappyza.wordpress.com/2010/08/08/getting-the-entity-framework-to-generate-an-interface-for-mocking/I've resolved problem for now... :)
Simon
+1. Really, all that needs to happen is to add `I` to the existing `ObjectSet...` declarations...
Igor Zevaka
+1  A: 

Quote in The Art Of Unit Testing by Roy Osherove:

There is no object-oriented problem that cannot be solved by adding a layer of indirection, except, of course, too many layers of indirection.

Below is my mockable EF4 POCO setup. I didn't use T4 as it was too hard to work out how to clean up the template to not generate too much gumpf. You can of course hack the T4 template to spit out something like this structure.

The trick was to create the ObjectSet<T>s manually and expose them as IQueryable. Because Add and Create are on ObjectSet<T>/ObjectSet<T>, I also had to add methods for adding and creating entities.

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
}

Now, you will note that the entity collection type on the interface is IQueryable<T>, as opposed to IObjectSet<T>. I couldn't be bothered creating FakeObjectSet and IQueryable provided me with enough flexibility. So in order to KISS, I've managed without it.

Mocking IQueryable, on other hand, is trivial:

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());
  }
}
Igor Zevaka