This article is cross-posted from my personal blog.
In the previous article in this blog series on creating testable modules, I began to create the View Links feature of our LinksMVP module. In this article I will add the concrete Repository class.
The LinksRepository Class
Listing 1 shows the LinksRepository class required to satisfy the ILinksRepository interface. Note the addition of a new interface – IDataService.
Listing 1 – The LinksRepository Class
|
1: public class LinksRepository : ILinksRepository
2: {
3: private IDataService dataService;
4:
5: public LinksRepository(IDataService dataService)
6: {
7: this.dataService = dataService;
8: }
9:
10: public List GetLinks(int moduleId)
11: {
12: return CBO.FillCollection(dataService.GetLinks(moduleId));
13: }
14: }
|
In the same way that we defined the Business Layer with an ILinksRepository interface, we define the Data Layer with the IDataService interface.
I am using IDataService (and DataService as the concrete implementation) as I intend to use the DAL+ – the DAL+ is a set of core DataProvider methods that allow me as a Module Developer to avoid having to create my own DataProvider/SqlDataProvider combination – we will see how this works when we get to the Data Layer.
I could bypass the use of a DataService layer in my module and just call the core DAL+ methods directly from the Repository class – but then I would have my Repository tightly-coupled with the DotNetNuke core Data Layer, and I would not be able to test it independently. In this way I can test my Repository by using a MockDataService.
Testing the LinksRepository Class
Listing 2 shows the test for testing the LinksRepository class. This test looks very similar to the test for the Presenter.
Listing 2 – Testing the LinksRepository
|
1: [TestMethod]
2: public void Repository_Should_Retrieve_All_Links_When_ModuleId_Is_Valid()
3: {
4: //First create a MockDataService
5: MockDataService dataService = new MockDataService();
6:
7: //Next create a LinksRepository
8: LinksRepository repository = new LinksRepository(dataService);
9:
10: //Get the Links from the Repository
11: List links = repository.GetLinks(MockHelper.ValidModuleId);
12:
13: Assert.AreEqual<int>(2, links.Count);
14: }
|
In line 5 we create a MockDataService (Listing 3), which returns a DataReader which we generate in the MockHelper.CreateLinksReader method (Listing 4). In line 8 we create our LinksRepository and pass it our MockDataService instance. We then call the GetLinks method in line 11 and assert that we did get the correct number of links returned.
Listing 3 – The MockDataService class
|
1: public class MockDataService : IDataService
2: {
3: public IDataReader GetLinks(int moduleId)
4: {
5: //Get the Mock Links Reader
6: return MockHelper.CreateLinksReader(moduleId);
7: }
8: }
|
Listing 4 – The CreateLinksReader method in MockHelper
|
1: public static IDataReader CreateLinksReader(int moduleId)
2: {
3: DataTable datatable = new DataTable();
4: datatable.Columns.Add("ModuleId");
5: datatable.Columns.Add("LinkId");
6: datatable.Columns.Add("LinkURL");
7: datatable.Columns.Add("Title");
8: datatable.Columns.Add("ViewOrder");
9:
10: for (int i = 1; i < 3; i++)
11: datatable.Rows.Add(moduleId, i, String.Format(LinkURL, i), String.Format(Title, i), i);
12:
13: return datatable.CreateDataReader();
14: }
|
So this test is fairly straightforward – we should get a green light (see Figure 1).
Figure 1 – Test Results with our new Links Repository Tests
|
|
But wait, we get a red light – our test fails. If we double-click on the failing test result we can find out why the test fails (See Figure 2).
Figure 2 – Failing Test Result Detail
|
|
So, what is the problem. Our LinksRepository calls the core CBO method FillCollection method. This method uses reflection to match the DataReader column names to the correct properties, but because of the expense of reflection calls, the CBO class uses caching, and the test fails because there is no CachingProvider to instantiate.
IoC Container to the Rescue
Prior to 5.0, this behaviour was a deal-breaker. All the providers are loaded by the core based on the web.config settings, so prior to 5.0 we would have to launch the web-browser in order to get a web context, to load our providers.
However, recognizing that this causes unnecessary coupling, in 5.0 we modified this behaviour by introducing a simple IoC (Inversion of Control) container, and in the Application_Start method of global.asax.vb all the providers are loaded to make them available.
If you look at the first and second lines of the stack trace in Figure 2 our error is happening because there is no Container and therefore no CachingProvider available.
But, in 5.0, it is simple enough to create an instance of the Container in our test method and add a CachingProvider instance to it (see Listing 5), so we can test the LinksRepository without worrying about a web context.
Listing 5 – The Revised LinskRepository Test
|
1: [TestMethod]
2: public void Repository_Should_Retrieve_All_Links_When_ModuleId_Is_Valid()
3: {
4: //Create a Container
5: ComponentFactory.Container = new SimpleContainer();
6:
7: //Create a File Based Cache Provider and register it in the Container as the CBO class
8: //in the DotNetNuke Framework requires a Caching Provider
9: ComponentFactory.RegisterComponentInstance(new FBCachingProvider());
10:
11: //Now create a MockDataService
12: MockDataService dataService = new MockDataService();
13:
14: //Next create a LinksRepository
15: LinksRepository repository = new LinksRepository(dataService);
16:
17: //Get the Links from the Repository
18: List links = repository.GetLinks(MockHelper.ValidModuleId);
19:
20: Assert.AreEqual<int>(2, links.Count);
21: }
|
When we do this we get three out of three green lights.
One more layer to go (the Data Layer) and then we can go back and build our actual LinksView UserControl. In the next article in this series I will build the DataService class and create a test for it.