Testing EF 4.0 with POCO and t4 templates
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
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
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());
}
}
链接地址: http://www.djcxy.com/p/47574.html
上一篇: 释放静态内存? 不,那不可能是正确的
下一篇: 用POCO和t4模板测试EF 4.0