When a Repository has a base, we can build its client only referencing to the base, instead of the concrete repository class, and exposing interface for DI (Dependency Injection). Then we can test the client with a mock Repository instance. That makes testing of the client code very easy, without really calling to the database.
Following code snippets shows the big picture.
class BlogRepoBase
{
// create a mockup BlogEntry list in memory
private List
//public override interfacing methods;
// shared protected methods;
}
If use Interface, we can take out the public interface members into a seperate Interface:
interface IBlogRepo
{
// public interfacing method
}
Nevertheless, we also need a base class in order to create the in-memory fake data instance and shared logics.
class BlogRepoBase
{
// create a mockup BlogEntry list in memory
private List
// shared protected methods;
}
Now we can create a variety of concrete Repositories inheriting from the base, e.g. a FakeBlogRepository
class FakeBlogRepository : BlogRepBase, IBlogRepo
{
// the implementation ...
}
Next, suppose a Controller uses the Repository, we put code like:
class BlogController: Controller
{
private IBlogRepo _rep;
public BlogController() : this(new RealBlogRepo()){ }
public BlogController(IBlogRepo rep){ this._rep = rep;}
}
Now we can test the controller,
[TestMethod]
public void ShowBlogEntriesTest()
{
// arrange
var rep = new FakeBlogRepo();
var ctr = new BlogController(rep);
// act
var result = (ResultView)ctr.Index();
// assert
CollectionAssert.AllItemsAreInstanceOfType()( (ICollection) result.ViewData.Model, TypeOf(BlogEntry));
}
Wow, you see how we have achieved testability with the Interface! Next, we take a look of creating the real Repository. There are different ways. A typical way is with EF(Entity Framework).
class EFBlogRepository : BlogRepBase, IBlogRepo
{
private BlogEntryEntity MapTo(BlogEntry entry)
{
return new BlogEntryEntity()
{
P1 = entry.P1,
P2 = entry.P2,
...
}
}
// public override repository interface methods
}
It is not uncommon that these public methods are implemented with EF functionalities and LINQ operations, but one may wonder why do we need the MapTo function? The simple reason is that we don't want to expose BlogEntryEntity that is created by EF and dependent on EF. We just want to constrain the XxEntryEntity classes within the repository layer, but not beyond. So we create XxEntry, that is used across application layers.
This totally makes sense, but there is an argument on how we define XxEntry classes. Would we define them as having exactly the same properties as XxEntryEntity, or would we defined them inline with the ViewModel? Theoratically, ViewModel works on top of XxEntry (suppose we call it Domain Model, while call XxEntryEntity Data Model), because View Model addresses the concern of Views and the related validation rules and so forth, while Domain Model is the data used to define business logics.
So we end up having DataModel, DomainModel and ViewModel. In practice, if there is not much business logic out there, people define Domain Model as ViewModel, because in this way we decreases redundancy of code.
This article has examined using RepositoryBase to achieve testability. Also, we examined DataModel, DomainModel and ViewModel and why we need mapping among them.