Reputation: 757
I have a method for which I have to write a Unit Test. This method uses Database context to retrieve details from database.
Now I am thinking of ways to mock this database context and test remaining LINQ code.
Similarly I have other methods which has some logic and some LINQ queries I want to Unit test them as well.
public class OrderService
{
private DataContext _context;
public OrderService(DataContext context)
{
_context = context;
}
public int OrderCount(int orderId)
{
var count = _context.Orders.Where(x=>x.orderId==orderId).ToList().Count;
}
}
How should I approach this situation?
This is common scenario faced by developers still there is not much information available on this. Any suggestions would be appreciated.
Upvotes: 1
Views: 1266
Reputation: 30454
My advice would be to separate the fact that your data is in a database from the way that you want to fetch your data.
In other words: your LINQ methods should have IQueryable<...>
as input. Not a DbContext.
Apart from the fact that this will make your unit testing a piece of cake, it will also allow you to reuse your LINQ handling for data from a different source, for instance a different database, or a CSV-file, or XML, or maybe just from a Dictionary.
I create an extension method. If you are not familiar with extension methods, consider to read Extension Methods Demystified
public static int Count(this IQueryable<Order> orders, int orderId)
{
return orders.Where(order=>order.orderId==orderId).Count();
}
Usage:
int orderId = this.ReadOrderId();
using (var dbContext = new MyOrderContext(...))
{
return dbContext.Orders.Count(orderId);
}
Maybe CountByOrderId
is a better name.
And for your unit test, for example to test the CountByOrderId of an empty order collection:
int orderId = 4;
var emptyOrderCollection = Enumerable.Empty<Order>();
int orderCount = emptyOrderCollection.CountByOrderId(orderId);
Assert.AreEqual(..., orderCount);
By the way, did you notice that I removed ToList
? It is a bit of wasting processing power to first create a List, and then only use the number of elements in it, then throw away the list.
If you want to make your count method even more generic, for instance to count also Products with a certain price, or Students of a certain BirthYear:
public static CountByProperty<TSource, TPropery>(this IQueryable<TSource> source,
Expression<Func<TSource,TProperty>> propertySelector,
TProperty value)
{
return source.Where(s=>propertySelector(s)==value).Count();
}
Usage:
return dbContext.Orders.CountByProperty(order => order.OrderId, 4);
Or count the Products that have a price of exactly $1.00:
return dbContext.Products.CountByProperty(product => product.Price, 1.00);
Upvotes: 1