views:

226

answers:

2

Let's say I have the following function I want to test:

public void CancelOrder(Order order)
{
    order.Status = "Cancelled";

    _emailService.SendEmail(order.User.Email, "Your order has been cancelled!");
}

Now, the Order class is a SubSonic generated class and the User property on it is lazy-loaded, meaning that when I call order.User.Email it actually runs a SQL statement to fetch the User.

If I wanted to unit test this, I would have issues because I don't really want my unit test hitting my database.

My current solution is to refactor the CancelOrder function to look like this:

public void CancelOrder(Order order)
{
    order.Status = "Cancelled";

    User user = _userRepository.GetByUserID(order.UserID);

    _emailService.SendEmail(user.Email, "Your order has been cancelled!");
}

Then I can stub out the _userRepository.GetUserByID() call to return a hardcoded User object.

Is this the best way to do this? I guess you could argue the second implementation is cleaner, because all data access is done via the repository instead of hidden within properties.

A: 

Define your own Order-alike interface that provides the bare-bones functionality you need and use that as the argument type for the CancelOrder method:

interface Order {
  public void Status(...); // or property
  public string UserEmail():
}

Then create a delegating implementation of that interface, e.g. SubsonicOrder, that calls down to the database. Use a stub implementation in your tests. I realize that you might need a richer model for the Order interface; this is just an example.

If needed, do the same with the email service stuff (perhaps by dependency injection through constructor or service locator).

Cwan
A: 

Why don't you want your unit test hitting your database? If you are using MBUnit to write your test case you could just mark the Unit Test with the RollBack attribute and any changes to the database will be rolled back.

I have always prefered writing unit test cases that target the Business Layer but that actually ping the database so that the Unit Test is most similar to what the application itself will be using.

runxc1 Bret Ferrier