Testing the Entity Framework 4.0 RIA Services Domain Service

Recently I’ve been working on refactoring and testing a Silverlight project that uses the EntityFramework.

Making all the changes of the auto-generated code in order to test it proved to be a harder task that I initially imagined.

This article summarises my findings and for getting the system ready for testing.

First of all, here is a list of links that helped me:

Testing out the Entity Framework WCF RIA Services Domain Service

WCF RIA Services Part 8 – Testing and Debugging

Testability and Entity Framework 4.0

However, none of these resources fully describe a working solution.

Here is how I finally got it working:

  • Step 1

Create an IUnitOfWork interface that will abstract the ObjectContext object used by your domain Service
Create additional operations the ObjectContext exposes as extension methods (e.g. AttachAsModified<TEntity>)

  • Step 2

Create an InMemoryUnitOfWork inheriting from IUnitOfWork (used for testing)
Create an InMemoryIObjectSet<T> inheriting from IObjectSet<T> (used for testing)

  • Step 3

Create a testing base class EFTestBaseClass for putting all the things together
Create a particular TestHelperClass for testing a particular test

There are additional important tweeks I will highlight in bold during the example.
Example:
Consider the following table in the entity container called NewTechCodeTestingEntities
An automatically generated Domain Service for manipulating it will look something like:

public class DomainService1 : LinqToEntitiesDomainService<NewTechCodeTestingEntities>
 {
 ...
 }

All the methods we are interested in testing will be hosted in this class. But instantiating it for testing will result in all operations being executed against the database. In order to avoid this situation we will have to somehow decouple this class form the EF.

(Step1)

First of we have to create the previously mentioned IUnitOfWork:

public interface IUnitOfWork
 {
 IObjectSet<Book> Books { get; }

ObjectStateManager ObjectStateManager { get; }
 int SaveChanges();
 }

This class abstracts the NewTechCodeTestingEntities.

This way we can create a special constructor for the Domain Service that we can use during testing:

public class DomainService1 : LinqToEntitiesDomainService<NewTechCodeTestingEntities>
 {
 new private readonly IUnitOfWork ObjectContext;

public DomainService1()
 {
 this.ObjectContext = base.ObjectContext;
 }

public DomainService1(IUnitOfWork unitOfWork)
 {
 this.ObjectContext = unitOfWork;
 }

...
 }

It’s important to notice how we created a new ObjectContext filed by using the new operator which either defaults to the base class ObjectContext (used for the production code when you call the parameterless contructor) or sets it to a specified one we create during testing.

An important thing to notice is the use of IObjectSet<Book> instead of ObjectSet<Book>.

The auto-generated file exposes the object in ObjectSet<T> collections.

In order to use the current approach you will have to manually change All the ObjectSet<T> declarations to IObjectSet<T> (Find and replace should do the trick in seconds)

The second thing you will have to do is make the NewTechCodeTestingEntities implement the IUnitOfWork.

The code will therefore look something like this:

public partial class NewTechCodeTestingEntities : ObjectContext, IUnitOfWork
 {
 ...
 }

Notice that these two steps (changing the collection declaration and inheriting from the IUnitOfWork) will have to be performed each time you update your EF model. I find this approach acceptable mostly because it is the only way I got the whole system to work.

Creating the extension methods are needed for keeping the auto generated code working. The only method I needed was AttachAsModified<T>:

public static class Extension
 {
 public static void AttachAsModified<TEntity&gt;(this IObjectSet<TEntity> objectSet, TEntity current, object original) where TEntity : class
 {
 throw new NotImplementedException();
 }
 }

(Step 2)

The following two classes were created for keeping the testing data:

public class InMemoryUnitOfWork : IUnitOfWork
 {
 public IObjectSet<Book> Books
 {
 get;
 set;
 }

public ObjectStateManager ObjectStateManager
 {
 get;
 set;
 }

public int SaveChanges()
 {
 throw new NotImplementedException();
 }
 }

and

public class InMemoryIObjectSet<T> : IObjectSet<T> where T : class
 {
 public InMemoryIObjectSet()
 : this(Enumerable.Empty<T>())
 {
 }

public InMemoryIObjectSet(IEnumerable<T> entities)
 {
 _set = new HashSet<T>();
 foreach (var entity in entities)
 {
 _set.Add(entity);
 }
 _queryableSet = _set.AsQueryable();
 }
 public void AddObject(T entity)
 {
 _set.Add(entity);
 }
 public void Attach(T entity)
 {
 _set.Add(entity);
 }
 public void DeleteObject(T entity)
 {
 _set.Remove(entity);
 }
 public void Detach(T entity)
 {
 _set.Remove(entity);
 }
 public Type ElementType
 {
 get { return _queryableSet.ElementType; }
 }
 public Expression Expression
 {
 get { return _queryableSet.Expression; }
 }

public IQueryProvider Provider
 {
 get { return _queryableSet.Provider; }
 }

public IEnumerator<T> GetEnumerator()
 {
 return _set.GetEnumerator();
 }

IEnumerator IEnumerable.GetEnumerator()
 {
 return GetEnumerator();
 }

readonly HashSet<T> _set;

readonly IQueryable<T> _queryableSet;
 }

If you are interested in finding more about these classes check the links at the beginning of the article.

(Step 3)

public class EFTestBaseClass
 {
 public EFTestBaseClass()
 {
 this.unitOfWork = new InMemoryUnitOfWork();
 this.controller = new DomainService1(unitOfWork);
 }

protected InMemoryUnitOfWork unitOfWork;
 protected DomainService1 controller;
 }

and

public class BooksTestHelper : EFTestBaseClass
 {
 protected IList<Book> books;

protected InMemoryIObjectSet<Book> repositoryBooks;

public BooksTestHelper()
 {
 this.PopulateCatalogueMaster();
 }

public void PopulateCatalogueMaster()
 {
 this.books = CreateCatalogueMasters().ToList();
 this.repositoryBooks = new InMemoryIObjectSet<Book&gt;(this.books);
 this.unitOfWork.Books = repositoryBooks;
 }

public static IEnumerable<Book> CreateCatalogueMasters()
 {
 yield return new Book()
 {
 Id = 1,
 Title = "Madame Bovary",
 Author = "Gustave Flauber"
 };
 yield return new Book()
 {
 Id = 2,
 Title = "The Lady of the Camellias",
 Author = "Alexandre Dumas, fils"
 };
 yield return new Book()
 {
 Id = 3,
 Title = "Notre Dame de Paris",
 Author = "Victor Hugo"
 };
 }
 }

allows us to create the following test:

 

[TestClass]
 public class BooksTest : BooksTestHelper
 {
 [TestMethod]
 public void GetBooks()
 {
 var books = controller.GetBooks();
 // insert test here
 }
 }

The tweaks that helped me get the system working were the observations highlighted in green which on top of the knowledge you get by reading the introduction articles should help you in testing your application.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s