Friday, January 30, 2009
This is the third post in the series. You can see the other to posts here:
- Creating a Web Application Using MVC, Unity and NHibernate – Part 1, Setting up Nhibernate
- Creating a Web Application Using MVC, Unity and NHibernate – Part 2, Simple Querying using Nhibernate
The Service Layer
Now that I have a data layer, I would like to create a service layer that takes the records retrieved from the database and then applies the business rules to them. Some of these rules are that I would like to apply are:
- I would only like to show the first 5 most recent posts on the home page.
- I would like catch any exceptions and log them.
If I was not testing this function first, my code would look something like this.
37 public List<NewsItemDto> GetLatestNewsItems(int numberOfItems)
39 List<NewsItemDto> items;
43 using (ISessionFactory sessionFactory = (new Configuration().Configure().BuildSessionFactory()))
46 using (ISession session = new object() as ISession)
48 var provider = new NHibernateDataProvider(session);
49 var result = provider.GetAllPublishedFrontPagePosts();
50 items = result != null ? result.OrderByDescending(i => i.DatePublished).Take(5).ToList() : null;
54 catch (Exception ex)
56 ExceptionPolicy.HandleException(ex, "General");
60 return items;
Here are some of the issues with this method.
- The method is depending on the NHibernateDataProvider class. This means I have to have a database setup for this test. If I have a lot of service layer tests connecting to the database then these tests are going to take forever to run.
- Another issue with being dependant on the NHibernateDataProvider class is now I have to some way to create an ISessionFactory class.
- For logging errors, I have a dependency on using Microsoft Exception Handler class so if I ever want to change that logging plumbing I have to change it all over my app.
· In general there is a lot of stuff happening but really all I want to test is getting a list of news items that match the count I want and are sorted in the correct order.
Okay, so back to the drawing board. Let me start with the test first and see if I can test this function without having it connect to a database. To do this I am going extract the interface for the NHibernateDataProvider class and call it IDataProvider.
7 public interface IDataProvider
9 IQueryable<NewsItemDto> GetAllPublishedFrontPagePosts();
10 NewsItemDto GetNewsItemByItemId(long newsItemId);
11 IList<AuthorDto> GetAuthorsBy(string userId);
In this example, I am using the Microsoft Enterprise Library Exception Policy class for logging exceptions. This class is sealed with one static method class called HandleException. Because of this, I cannot extract an interface, so for now I am going wrap this class in another concrete class that implements an interface that I can inject.
The interface looks like this:
5 public interface ILogger
7 void LogException(Exception ex);
The derived concrete class looks like this:
6 public class MicrosoftLogger : ILogger
8 public void LogException(Exception ex)
10 ExceptionPolicy.HandleException(ex, "General");
Now I can mock my data provider and my logging class in my test, and I can also inject these classes into my web application later on.
So now the constructor of my service class looks like this:
12 public class NewsItemService : INewsItemService
14 private readonly IDataProvider newsDataProvider;
15 private readonly ILogger logger;
17 public NewsItemService(IDataProvider newsDataProvider, ILogger logger)
19 this.newsDataProvider = newsDataProvider;
20 this.logger = logger;
So I mentioned that I am going mock the data provider class and to do this I am going use my mock tool of choice Rhino.Mocks.
75 public void NewsItemService_get_latest_news_items_should_return_5_most_recent()
77 var mockRepository = new MockRepository();
78 var dataProvider = mockRepository.StrictMock<IDataProvider>();
79 var mockLogger = mockRepository.StrictMock<ILogger>();
81 using (mockRepository.Record())
86 var numberOfItems = 5;
87 var expected = 5;
88 using (mockRepository.Playback())
90 var target = new NewsItemService(dataProvider, mockLogger);
91 var items = target.GetLatestNewsItems(numberOfItems);
92 var currentDate = DateTime.MaxValue;
94 foreach (var item in items)
96 Assert.IsTrue(currentDate > item.DatePublished, currentDate.ToShortDateString() + " is not > " + item.DatePublished.ToShortDateString());
97 currentDate = item.DatePublished;
100 Assert.AreEqual(items.Count, expected, "Get the latest news does not eaqual 5");
In the test above instead of connecting to the database, I first tell Rhino Mocks to mock my data provider. I pass it the IDataProvider interface and Rhino Mocks gives me back an instantiated data provider even though there is now derived concrete class involved. I then do the same for the logging object.
In the Record section, I am telling Rhino Mocks that later on when I actually test my service object to expect it to make a call to the data provider class with a specific set of parameters (in this case I have no parameters) and when this occurs return my mocked result. Once I have recorded all my expected calls that I want to mock, I can then proceed to the Playback method to test my service.
Inside the Playback I write my test as if I was actually connecting to a database and I assert that I got back 5 records sorted by the published date.
So I test and my test fails because I have not implemented the GetLatestNewsItems yet so let me do that.
23 public List<NewsItemDto> GetLatestNewsItems(int numberOfItems)
27 var items = newsDataProvider.GetAllPublishedFrontPagePosts();
28 return items != null ? items.OrderByDescending(i => i.DatePublished).Take(5).ToList() : null;
30 catch(Exception ex )
Now I can test my object without being dependant on any concrete classes, and I can also inject my dependencies later on using Unity.In my next post I will look into testing my controller class and some of the features MVC provides to do controller unit testing.