Unit testing is a tricky beast. As with all unit tests it is important to abstract any dependencies – so in the case of EF it’s the data persistence source. This must be handled carefully because EF creates a container that performs the interaction with the data source. Abstracting that container (via mocking or stubbing) is painfully hard.
There are a few approaches for doing this, this is mine. I’m using the following tooling:
- Visual Studio 2010
- Microsoft Moles
The pattern uses the following DAL class structure (like a pseudo-repository pattern against an example Product database table):
- ProductDataRepository: IProductDataRepository – A thin layer to EF, returns only IQueryable<T> types from raw EF lookups. No filters, sorting or other queries are applied here as this class is not unit testable. This class internal to the DAL assembly.
- ProductRepository(IProductDataRepository repos) – Constructor takes an instance of the IProductDataRepository interface, and uses this for executing specific queries against the base IQueryable<T> values. This class is exposed for external calls. This is unit-tested.
The unit tests can use Moles to stub an instance of IProductDataRepository (delegating the internal Get… methods to returning a hard coded collection of test data) that is then passed to the instance of ProductRepository under test.
Looking at these classes from the bottom up, as an example:
ProductDataRepository
To Provide a thin layer that only returns IQueryable<T> (our example also filters out deleted rows):
public class ProductDataRepository : IProductDataRepository {
private MyDatabase_ModelContainer _model;
protected internal MyDatabase_ModelContainer Model {
get {
if (_model == null) {
_model = new MyDatabase_ModelContainer();
}
return _model;
}
}
public IQueryable<product> GetProducts() {
return Model.Products.Where(p => p.IsTombstoned == false);
}
}
ProductRepository
Core Requirement: Provide the implementation and execution of the data lookup. Consumes an instance of IProductDataRepository via constructor injection.
public class ProductRepository{
public ProductRepository() : this(new ProductDataRepository()) {
}
private IProductDataRepository _mRepository;
public ProductRepository (IProductDataRepository iRepository) {
_mRepository = iRepository;
}
public Product GetProduct(String productKey) {
return mRepository.GetProducts().FirstOrDefault(p => p.ProductKey.Contains(productKey));
}
public List<Product> GetProducts (String productKey) {
return mRepository.GetProducts().Where(p => p.ProductKey.Contains(productKey).ToList();
}
}
So, we can see that this is making calls to:
mRepository.GetProducts()
Which returns an IQueryable<T> type, then implements required functionality:
.Where(p => p.ProductKey.Contains(productKey))
and then executes the Query:
.ToList();
GetProductsTest
To test the ProductRepository we can create an instance and stub the IProductDataRepository. The mocking and standard setup is done in an abstract class, to provide reusability for other tests that may require this functionality:
[TestClass]
public abstract class ProductTestContext
{
private IProductDataRepository _dataRepository;
private IProductDataRepository DataRepository
{
get {
if (_dataRepository == null) {
_dataRepository = new SIProductDataRepository() {
GetProducts = () => Products.AsQueryable()
};
}
return _dataRepository;
}
}
private ProductRepository _ProductRepository;
public ProductRepository ProductRepository
{
get {
if (_ProductRepository == null) {
_ProductRepository = new ProductRepository(DataRepository);
}
return _ProductRepository;
}
}
private List<Product> _products;
public List<Product> Products
{
get {
if (_products == null) {
_products = new List<Product>();
for (int i = 1; i <= 9; i++) {
_products.Add(new Product(){ ProductKey = "MyKey" + i });
}
}
return _products;
}
}
}
So, here we can see the IProductDataRepository readonly property returns a stubbed instance new new SIProductDataRepository. This stub is generated by the Moles framework (see Moles documentation on how to do this). During the creation of this object we delegate the GetVouchers method to return the concrete (and hardcoded because this is a unit test) instance of Products.AsQueryable():
GetProducts = () => Products.AsQueryable()
The .AsQueryable() is essential to insure the instance of IProductDataRepository, that is used during the test, returns the same type as the instance used in the real world.
Now our unit test class can inherit ProductTestContext and Arrange, Act and Assert the required tests easily:
[TestClass]
public class GetProductsTest : ProductTestContext
{
[TestMethod]
public void GetSingleProduct()
{
// repeat the test for each instance in the test collection
for (int i = 1; i <= 9; i++) {
// Arrange – most of the Arrangement is done in the abstract
var goodProductKey = "MyKey" + i;
// Act – on the abstract instance of ProductRepository,
// which is using the stubbed instance of ProductDataRepository
var product = ProductRepository.GetProduct(goodProductKey);
// Assert – make sure the correct product is returned from the stubbed data
Assert.IsNotNull(product);
Assert.IsTrue(product.ProductKey.Equals(goodProductKey));
}
}
[TestMethod]
public void GetIndivudalFilteredProducts()
{
// repeat the test for each instance in the test collection
for (int i = 1; i <= 9; i++) {
// Arrange – most of the Arrangement is done in the abstract
var goodProductKey = "MyKey" + i;
// Act – on the abstract instance of ProductRepository,
// which is using the stubbed instance of ProductDataRepository
var products = ProductRepository.GetProducts(goodProductKey);
// Assert – make sure the correct products are returned from the stubbed data
Assert.IsNotNull(products);
Assert.IsTrue(products.Count == 1);
}
}
[TestMethod]
public void GetMultipleFilteredProducts()
{
// Expectation – all the results in the abstract Products collection should be returned
var expectation = Products.Count;
// Arrange – most of the Arrangement is done in the abstract
var goodProductKey = "MyKey";
// Act – on the abstract instance of ProductRepository,
// which is using the stubbed instance of ProductDataRepository
var products = ProductRepository.GetProducts(goodProductKey);
// Assert – make sure the correct products are returned from the stubbed data
Assert.IsNotNull(products);
Assert.AreEqual(expectation, products.Count);
}
}

