This article is cross-posted from my personal blog
In the previous article in this blog series on creating testable modules, I introduced the sample DotNetNuke module that we will build – Links MVP. This is a simpler version of the core DotNetNuke Links module. In this article we will build our first tests.
Test Driven Development – TDD
In the first article in this blog series I discussed a number of core concepts including the concept of Test Driven Development (TDD). In this development paradigm you write the test first, and your tests become the Specification. More specifically the process can be summarized as follows (from Test-Driven Development, By Example – Kent Beck)
- Write a test
- Run all tests – and see the new test fail
- Make a little change – to make the test pass
- Run all tests and see them all succeed
- Refactor to remove duplication
- Repeat
I don’t necessarily intend to do the whole project in this way, as the focus of this blog series is not Test Driven Module Development per se, but creating Testable Modules. But it is an interesting experiment.
Our First Test
The first test we will write is shown in Listing 1. This test is designed to simulate that when the ViewLinks view is given a valid ModuleId (ie a ModuleId for a Links module that has some links defined) the View will display all the Links.
Listing 1 – The First Test
|
1: [TestMethod]
2: public void View_Should_Display_All_Links_When_ModuleId_Is_Valid()
3: {
4: //First Create the Mock View and a Mock Repository
5: MockLinksView view = new MockLinksView();
6: MockLinksRepository repository = new MockLinksRepository();
7:
8: //Create the Presenter
9: LinksViewPresenter presenter = new LinksViewPresenter(view, repository);
10:
11: //Set the Views Module Id
12: view.ModuleId = MockHelper.ValidModuleId;
13:
14: //Call the presenters OnViewLoaded method to simulate the Page Load
15: presenter.OnViewLoaded();
16:
17: //Assert that the View displays two Links
18: Assert.AreEqual(2, view.Links.Count);
19: }
|
So lets look at this test. In lines 4 and 5 we create two Mock objects a MockLinksView and a MockLinksRepository. In line 9 we then pass both of these Mock objects in the constructor to create a LinksViewPresenter. We use Mock objects here as we are testing the Presenter.
In line 12 we set the View’s ModuleId property to a “valid” Id. As with the HelloWorld example we call the presenters OnViewLoaded method and then we assert that the number of links in the view’s Links property is 2.
In this test we have two Mock objects and one object that is part of the LinksMVP project – the LinksViewPresenter. Listing 2 shows this LinksViewPresenter class.
Listing 2 – The LinksViewPresenter Class
|
1: public class LinksViewPresenter
2: {
3: ILinksView view;
4: ILinksRepository repository;
5:
6: public LinksViewPresenter(ILinksView view, ILinksRepository repository)
7: {
8: this.view = view;
9: this.repository = repository;
10: }
11:
12: public void OnViewLoaded()
13: {
14: view.Links = repository.GetLinks(view.ModuleId);
15: }
16: }
|
The smart readers will have already noticed something different from the Hello World example, the addition of the IRepository interface. Otherwise this class is similar to the HelloWorldPresenter.
The Repository Pattern
There are two strategies that can be used in the Business Layer, for handling the logic involved with creating, retrieving, updating and deleting (CRUD) the business objects. In the first strategy, the business objects have methods which handle the logic, in the second a separate class, called a Repository, focuses on the business logic keeping the objects themselves lightweight.
DotNetNuke developers will recognize this pattern as the Controller classes are essentially “Repositories”. In this blog series I will use the term Repository – to clearly differentiate from the Controller found in the MVC pattern or the MVP Supervising Controller pattern.
So ILinksRepository is obviously an interface that describes this logic. We use an interface so we can create Mock versions of the interface (see Listing 3). It contains a single method GetLinks, which retrieves a List of Link objects.
Listing 3 – The ILinksRepository Interface
|
1: public interface ILinksRepository
2: {
3: List GetLinks(int moduleId);
4: }
|
In our test we create a MockLinksRepository (see Listing 4). We do this because the test is designed to just test the Presenter. We will write a test for the Repository in a later article.
Listing 4 – TheMockLinksRepository class’s GetLinks method
|
1: public List GetLinks(int moduleId)
2: {
3: List links;
4: switch (moduleId)
5: {
6: case MockHelper.ValidModuleId:
7: //Valid Module Id - Create a List with two Links
8: links = new List();
9: links.Add(MockHelper.CreateLink(1, moduleId));
10: links.Add(MockHelper.CreateLink(2, moduleId));
11: return links;
12: default:
13: //Any other Module Id - return an empty List
14: links = new List();
15: return links;
16: }
17: }
|
This method takes a moduleId and returns a List of links (created using a helper function, Listing 5), as long as the moduleId is valid.
Listing 5 – The MockHelper.CreateLink method
|
1: public static Link CreateLink(int linkId, int moduleId)
2: {
3: //Create a Link with the linkId and moduleid provided
4: Link link = new Link()
5: {
6: LinkId = linkId,
7: ModuleId = moduleId,
8: };
9: return link;
10: }
|
Note that I am using the new syntax in C# 3.0 to create a Link object (see Listing 6) and set its two properties (LinkId and ModuleId). The two Links are added to a List and returned (Listing 4 – line 11).
Listing 6 – The Link Class
|
1: public class Link : ILink
2: {
3: private int linkId;
4: private int moduleId;
5:
6: public int LinkId
7: {
8: get { return linkId; }
9: set { linkId = value; }
10: }
11:
12: public int ModuleId
13: {
14: get { return moduleId; }
15: set { moduleId = value; }
16: }
17: }
|
The View
Finally we will look at the View. Our test requires us to set the ModuleId and then check the Links property. Listing 7 shows the ILinksView interface and Listing 8 shows the MockLinksView.
Listing 7 – The ILinksView Interface
|
1: public interface ILinksView
2: {
3: int ModuleId { get; set; }
4: List Links { set; }
5: }
|
Listing 8 – The MockLinksView Class
|
1: class MockLinksView : ILinksView
2: {
3: private List links;
4: private int moduleId;
5:
6: public List Links
7: {
8: get { return links; }
9: set { links = value; }
10: }
11:
12: public int ModuleId
13: {
14: get { return moduleId; }
15: set { moduleId = value; }
16: }
17: }
|
We should now have everything we need to run the tests. If we run the test we will get a green light, as we did add two links in our Mock Repository class.
Our Second Test
So now we have a passing test, we need to write a second test (Listing 9).
Listing 9 – The Second Test
|
1: [TestMethod]
2: public void View_Should_Display_No_Links_When_ModuleId_Is_Not_Valid()
3: {
4: //First Create the Mock View and a Mock Repository
5: MockLinksView view = new MockLinksView();
6: MockLinksRepository repository = new MockLinksRepository();
7:
8: //Create the Presnter
9: ViewLinksPresenter presenter = new ViewLinksPresenter(view, repository);
10:
11: //Set the Views Module Id
12: view.ModuleId = MockHelper.InValidModuleId;
13:
14: //Call the presenters OnViewLoaded method to simulate the Page Load
15: presenter.OnViewLoaded();
16:
17: //Assert that the View displays 0 Links
18: Assert.AreEqual(0, view.Links.Count);
19: }
|
The second test simulates the case where we pass an invalid Module Id (a ModuleId which has no links defined). In this test we check that the Links property of the View is empty. We now have two green lights and our code coverage is pretty good already.
Figure 1 – Code Coverage from our Two Tests
|
|
We have good code coverage because we have only written code that we need. This is one of the advantages of the TDD process as it encourages you to only do enough to pass the test. In this case the only area of the project we haven’t got code coverage of are the getters of the Link class.
Updating our First Test
We know what the values of the two links we passed to the LinksView are, as we create them in the Mock Repository class, so we can update our first test (Listing 10), so we can get 100% code coverage (Figure 2)
Listing 10 – The Modified First Test
|
1: [TestMethod]
2: public void View_Should_Display_All_Links_When_ModuleId_Is_Valid()
3: {
4: //Set the Views Module Id
5: view.ModuleId = MockHelper.ValidModuleId;
6:
7: //Call the presenters OnViewLoaded method to simulate the Page Load
8: presenter.OnViewLoaded();
9:
10: //Assert that the View displays two Links
11: Assert.AreEqual(2, view.Links.Count);
12:
13: //Assert that the first link has a LinkId of 1 and a Module Id of ValidModuleId
14: Assert.AreEqual(1, view.Links[0].LinkId);
15: Assert.AreEqual(MockHelper.ValidModuleId, view.Links[0].ModuleId);
16: }
|
Note that I have now moved the code that is common to both tests to the Test class’s constructor (This is an example of the Refactor phase of the TDD cycle)
Figure 2 – Updated Code Coverage from our Two Tests
|
|
So we now know that our Presenter works – it retrieves the Links and passes them to the View. Our Link model is incomplete, we only have a LinkId property and a ModuleId property, so that is not very useful, but it is a fairly trivial task to add more properties to the object, update the MockHelper.CreateLink method and update our test to assert that the values are correct, and finally to run the tests to make sure everything still works.
Note: It should be noted here that some developers who use TDD say that you don’t need to test simple getters and setters, but it is quite easy to make typos in the simplest of code, so I have included assertions for each property.