Monday, July 10, 2017

The Model Exists But The Database Is Empty: Unit Test With A Mocking Framework Is Best ROI

The Model Is Ready But No Data: Consider Mocking

I am sure you have encountered this scenario rather often especially in the early stages of a project: the model is present, the database was created but there is no test data. 


"Oooooo!  I’ll blast yall tables off with seed data varmints!"

If we are using Entity Framework Code-First then we could seed data during the database initialization process.  However, in the case that a DB initialization is not possible, the data could still be seeded following a different mechanism.  Assuming that I was working on a system based on the Northwind database, and the database was empty then data could be seeded and the process is very simple:


 public async Task SeedDataAsync()  
{
if(!_context.Suppliers.Any())
{
var category = _context
.Categories
.FirstOrDefault(c => c.CategoryName.Equals("Beverages"));
if(category == null)
{
category = new Category
{
CategoryName = "Beverages",
Description = "Soft drinks, coffees, teas, beers, and ales"
};
}
var supplier = new Supplier
{
CompanyName = "Joe's One Stop",
ContactName = "Joe Marino",
Address = "123 Main St.",
City = "Ottawa",
Region = "ON",
PostalCode = "O6T 3P9",
Country = "Canada",
Phone = "613-555-1232",
HomePage = "www.a1mansoftware.com",
Products = new List<Product>
{
new Product() { ProductName = "Sasquatch Ale", QuantityPerUnit = "24 - 12 oz bottles", UnitPrice = 14.00m, UnitsInStock = 111, Category = category },
new Product() { ProductName = "Steeleye Stout", QuantityPerUnit = "24 - 12 oz bottles", UnitPrice = 18.00m, UnitsInStock = 20, Category = category },
new Product() { ProductName = "Laughing Lumberjack Lager", QuantityPerUnit = "24 - 12 oz bottles", UnitPrice = 13.50m, UnitsInStock = 52, Category = category }
}
};
if (category.CategoryID == 0 ) _context.Categories.Add(category);
_context.Suppliers.Add(supplier);
await _context.SaveChangesAsync();
}
}

then call it when the system initializes, or in a startup sequence like this:

seeder.SeedDataAsync().Wait();  

I have not tested the above seed but it encapsulates the general idea.

Seeding data has its use and benefits because it allows to create a base data on which development can start and progress can be shown fairly quickly and the software development process shows advance.

However, there are scenarios where trying to seed data for the lack of it might be counterproductive and as a result we find ourselves losing perspective as to what the software we are building is trying to accomplish.  To seed the needed data we dedicate massive amounts of time with the hope to get our development moving forward; data that at the end of the day is thrown away.  Even when we have managed to seed data I have encountered scenarios where other developers have modified the data for one reason or another and now the feature under development or unit tests does not work anymore.

Mocking frameworks can help us abstract dependencies to a data access layer.  Using mocking frameworks we can create these mock object which can be setup to provide data needed to develop and test a feature and never access the database.

Using mock objects also allows unit tests to isolate themselves, hence, the feature under development can be tested carefully.

In my previous note, Testing EF 6.x Async Queries With Moq: In Memory DbAsyncQueryProvider using mock objects allowed testing Task<IQueryable<CustomerOrderStatistic>> CustomerOrderSummaryAsync(   CustomerOrderSearch criteria) without accessing the database because the data required for the method under test was provided by mocking repositories required by the service.  Hence the development of the feature was done in isolation.  Here are the tests that were designed and executed:


Moq framework used to mock DbSet and repositories


Conclusion

The take home message is that using mocking frameworks will force you to start developing a Unit Test infrastructure, and the benefits that we collect from such infrastructure are long lasting.  For instance:
  1. Mock objects helps greatly in a Test Driven Development process.  It takes time and practice to develop a TDD state of mind.  
  2. Test new features in isolation, helping test coverage.
  3. Abstract data access layer: the functionality can be tested without the need of seeded data.
  4. As a result of the above item; developing functionality which depends on the implementation of other dependencies can be undertaken because those dependencies can be mocked.  (mocking repositories, the above unit tests does not need the implementation of repositories dependencies only their definitions.
  5. As a result of conclusion 2 and 3; good SOLID Object Oriented principles become pivotal hence if dependencies are very hard to mock then the original design start to release its own smell.
This post is not trying to say that seed data to help us develop new features is a bad thing.  Seeding data has its own objectives and is a powerful tool at our disposal, however, we should not depend on seeded data to be able to develop new functionality :  mocking framework and TDD together are great tools to consider.

Testing EF 6.x Async Queries With Moq: In Memory DbAsyncQueryProvider

Motivation

The objective of this post is to test an asynchronous service operation, and in particular methods which include Entity Framework 6.x asynchronous queries.  When conducting unit testing of asynchronous code my starting point had to be a post by Stephen Cleary.  This article introduces the reader to the subject. providing general guidelines, what to avoid, valuable insights, etc.  His blog is also a valuable resource when undertaking asynchronous programming.


Code to be Tested

The following methods in based on the Northwind database and it computes the running totals for each customer of the store including the number of orders and how much they have spent historically.  The method is an operation of a service called CustomerOrderAggregation and it takes a search criteria containing CustomerID.  The idea is if the search criteria CustomerId value is set then the computation is executed for that customer and if not set then the computation is done for all customers.

  public async Task<IQueryable<CustomerOrderStatistic>> CustomerOrderSummaryAsync(  
CustomerOrderSearch criteria)
{
var result = await customerRepository
.GetAll()
.AsNoTracking()
.WhereIf(
!string.IsNullOrEmpty(criteria.CustomerId),
r => r.CustomerID.Equals(criteria.CustomerId))
.GroupJoin(
OrderOrderDetailsStatistics(criteria),
c => c.CustomerID,
o => o.CustomerId,
(c, o) => new { c, o })
.SelectMany(
x => x.o.DefaultIfEmpty(),
(x, l) => new CustomerOrderStatistic
{
CustomerId = x.c.CustomerID,
ContactName = x.c.ContactName,
TotalPayments = l != null ? l.TotalPayments : 0,
TotalOrders = l != null ? l.TotalOrders : 0,
}).ToListAsync();
return result.AsQueryable();
}


OrderOrderDetailsStatistics(criteria) executes a join between Order and Order_Details on OrderId and groups by Order.CustomerId and this is how we learn the running totals:


 private IQueryable<CustomerOrderGrouped> OrderOrderDetailsStatistics(  
CustomerOrderSearch criteria)
{
return orderRepository.GetAll()
.AsNoTracking()
.Join(
this.orderDetailsRepository.GetAll().AsNoTracking(),
o => o.OrderID,
od => od.OrderID,
(o, od) => new { o, od })
.GroupBy(g => new { g.o.CustomerID })
.Select(grp => new CustomerOrderGrouped
{
CustomerId = grp.Key.CustomerID,
TotalPayments = grp.Sum(row => (row.o.Freight.HasValue ? row.o.Freight.Value : 0) +
(row.od.UnitPrice * row.od.Quantity)),
TotalOrders = grp.Select(row => row.o.OrderID)
.Distinct()
.Count()
})
.WhereIf(
!string.IsNullOrEmpty(criteria.CustomerId),
r => r.CustomerId.Equals(criteria.CustomerId));
}

Unit Testing with Moq

The service construction is as follows:

 public CustomerOrderAggregation(  
ICustomerRepository customerRepository,
IOrderDetailsRepository orderDetailsRepository,
IOrderRepository orderRepository)
{
this.customerRepository = customerRepository;
this.orderDetailsRepository = orderDetailsRepository;
this.orderRepository = orderRepository;
}


hinting that for the unit tests using Moq three repositories need to be mocked.  Repositories GetAll() is a generic which returns an IQueryable therefore to be able to execute the queries against the mocked IQueryable GetAll() IQueryable needs to be implemented.  This is explained in the reference provided in this post:

 var data = new List<Customer>  
{
new Customer
{
CustomerID = "LAUGB",
CompanyName = "Laughing Bacchus Wine Cellars",
ContactName = "Yoshi Tannamuri",
ContactTitle = "Marketing Assistant"
}.AsQueryable();
};
var mockSet = new Mock<DbSet<Customer>>();
mockSet.As<IQueryable<Customer>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Customer>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Customer>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Customer>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());
customerRepositoryMock.Setup(c => c.GetAll()).Returns(mockSet.Object);

OrderRepository and OrderDetailsRepository GetAll() would need to me mocked using the same procedure for Order and Order_Details respectively.  However, trying to use the above mocked repositories will fail for two reasons.

First AsNoTracking() needs to be mocked as well:

mockSet.As<IQueryable<Customer>>()Setup(c => c.AsNoTracking()).Returns(dbSet.Object);

However, the test for the async method would fail with the following exception:

"System.InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068."

The solution to the error is in the link provided in the exception message!!!  The reference is telling us that to be able to process the async method a DbAsyncQueryProvider needs to be created.  I selected to follow the same solution discussed in the msdn post because it is reusable.  The only difference is that the mocked DbSet was encapsulated in a static class with a static generic method to facilitate reusability.


 public static class MockDbSet  
{
public static DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IDbAsyncEnumerable<T>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(queryable.GetEnumerator()));
dbSet.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(queryable.Provider));
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
dbSet.Setup(c => c.AsNoTracking()).Returns(dbSet.Object);
return dbSet.Object;
}
}


Now repositories are mocked this way in the test setup method:

 [TestInitialize]  
public void Setup()
{
// Arrange - Mock Repositories
var customerRepositoryMock = new Mock<ICustomerRepository>();
var orderDetailsRepositoryMock = new Mock<IOrderDetailsRepository>();
var orderRepositoryMock = new Mock<IOrderRepository>();
// Arrange - Setup return values
customerRepositoryMock.Setup(c => c.GetAll()).Returns(MockDbSet.GetQueryableMockDbSet<Customer>(
new Customer
{
CustomerID = "LAUGB",
CompanyName = "Laughing Bacchus Wine Cellars",
ContactName = "Yoshi Tannamuri",
ContactTitle = "Marketing Assistant"
},
new Customer
{
CustomerID = "JMARINO",
CompanyName = "Marino's",
ContactName = "Jose Marino",
ContactTitle = "Principal"
}));

// Arrange - setup order details
orderDetailsRepositoryMock.Setup(o => o.GetAll()).Returns(MockDbSet.GetQueryableMockDbSet<Order_Detail>(
new Order_Detail { OrderID = 1, ProductID = 23, UnitPrice = 7.20m, Quantity = 10 },
new Order_Detail { OrderID = 1, ProductID = 41, UnitPrice = 7.70m, Quantity = 20 },
new Order_Detail { OrderID = 1, ProductID = 77, UnitPrice = 10.40m, Quantity = 5 },
new Order_Detail { OrderID = 2, ProductID = 24, UnitPrice = 4.50m, Quantity = 5 },
new Order_Detail { OrderID = 2, ProductID = 52, UnitPrice = 7.00m, Quantity = 5 },
new Order_Detail { OrderID = 3, ProductID = 13, UnitPrice = 6.00m, Quantity = 7 },
new Order_Detail { OrderID = 3, ProductID = 25, UnitPrice = 14.00m, Quantity = 5 },
new Order_Detail { OrderID = 3, ProductID = 70, UnitPrice = 15.00m, Quantity = 5 }));
// Arrange - setup orders
orderRepositoryMock.Setup(
o => o.GetAll())
.Returns(
MockDbSet.GetQueryableMockDbSet<Order>(
new Order { OrderID = 1, CustomerID = "LAUGB", Freight = 4.65m },
new Order { OrderID = 2, CustomerID = "LAUGB", Freight = 0.94m },
new Order { OrderID = 3, CustomerID = "LAUGB", Freight = 4.33m }));
// Arrange - create instance of service and inject mocked repositories
_customerOrderAggregationService = new CustomerOrderAggregation(
customerRepositoryMock.Object,
orderDetailsRepositoryMock.Object, orderRepositoryMock.Object);
}

and the service is tested like this:

     [TestMethod]  
public async Task Test_CustomerOrderSummaryAsync_ForExcistingUser_Returns_Summary_With_One_Group()
{
// Arrange - Create search criteria
var criteria = new CustomerOrderSearch
{
CustomerId = "LAUGB"
};
// Act - Call Service async method
var result = await _customerOrderAggregationService
.CustomerOrderSummaryAsync(criteria);
// Assert - validate only one group is present
var resultList = result.ToList();
Assert.IsTrue(resultList.Count == 1);
}

Conclusion

  1. Mocking IQueryable using Moq for a repository of T the mocked DbSet IQueriable needs to setup: query Provider, query Expression, query ElementType, GetEnumarator()
  2. When testing async queries the mocked DbSet needs to set up e GetEnumerator() for an IDbAsyncEnumerator and setup and AsyncQueryProvider

References

  1. Async Programming: Unit Testing of Asynchronous Code: https://msdn.microsoft.com/en-us/magazine/dn818493.aspx 
  2. Explanation to InvalidOperationException when testing EF 6.x async queries: https://msdn.microsoft.com/en-gb/data/dn313107
  3.  Detailed explanation and reference code to be able to use moq mock objects in blocking and non blocking scenarios:  https://msdn.microsoft.com/en-gb/data/dn314429

A1 Repo: Simple Pagination for WCF Service Operation

It is never a good idea to paginate on the client.  This post is about a simple pagination for a WCF service operation.  For this work I nee...