Creating An Application, the TDD Approach
The previous post in this series covered downloading and installing the NUnit testing library for .NET. In this post we will:
• Create a new class library project
• Create a new unit testing project
• Create a new test
• Build the solution
• Pass the test
So let’s get started.
Launch Microsoft Visual Studio and create a new “Class Library” project and name it “BookStore”.
Delete the existing Class1.cs file.
Right-Click on the Solution in the Solution Explorer and Select “Add > New Project”.
Select a “Class Library” project and name it “BookStoreTests”.
Once again, delete the existing Class1.cs file.
Now that the environment is ready,
Right-Click on the References item under the BookStoreTests project tree and Select “Add Reference”
Since we will be testing the BookStore project, we will need to add a reference to it.
Select the Projects tab, then Select the BookStore project from the list and Click OK.
Now that leaves one more reference to add; the reference to the NUnit Framework library.
Right-Click on References again and Select “Add Reference”.
This time Select the .NET tab and scroll down to the nunit.framework assembly, Select it, and Click OK.
Now we are completely set-up and ready to begin coding.
At this point if you were able to follow along, your Solution Explorer should resemble the one below.
Now to create a test.
Right-Click on the BookStoreTests project and follow the “Add” > “New Item” menu sequence.
Select “Class” as the template and name it “BookStoreControllerTests.cs” and Click “Add”.
The reason that we named our class as such is, one popular design pattern is to place the UI, business logic, and database features in separate layers (projects or files).
So in our design, we will implement a BookStoreController that will represent our business logic layer. The controller’s job is to provide data/objects to our UI layer for display. Therefore, we want to test that our controller (business logic layer or bll) is doing just that, hence the BookStoreControllerTests.cs class.
Next we need to let the compiler know that we would like to use classes and methods provided by the nunit.framework library and the BookStore project that we referenced a moment ago.
Do this by adding using directives to the top of the BookStoreControllerTests.cs class and a special attribute to let nunit know to run the tests in the file as such:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using BookStore;
namespace BookStoreTests
{
[TestFixture]
public class BookStoreControllerTests
{
}
}
Next we need to add another nunit specific feature referred to as a setup method. A setup method is identified by the [SetUp] attribute. Set up methods allow you to prepare your objects/classes for testing, so during setup you can create instances of the actual classes that you will test and, if necessary, load data.
We will use ours to create an instance of our BookStoreController.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using BookStore;
namespace BookStoreTests
{
[TestFixture]
public class BookStoreControllerTests
{
//here we know that we are testing the BookStoreController
//so we need to declare one.
BookStoreController Controller;
//during the setup for our tests we want to actually instantiate the controller;
[SetUp]
public void Build()
{
Controller = new BookStoreController();
}
}
}
And now for the actual test method itself. Using NUnit, a test method is identified by the [Test] attribute. The accepted nomenclature for test methods, while verbose, identifies the expected result. For this test, we would like for the BookStoreController to return a particular book when passed an ISBN. For the purposes of this tutorial I will use a book from my personal library, “Windows Communication Foundation Unleashed”. No big deal, we could very well have made up a book title and ISBN.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using BookStore;
namespace BookStoreTests
{
//let nunit know that this is a test class. So run tests inside.
[TestFixture]
public class BookStoreControllerTests
{
//here we know that we are testing the BookStoreController
//so we need to declare one.
BookStoreController Controller;
//Create a setup for tests that instantiate the controller
//and add data if necessary.
[SetUp]
public void Build()
{
//create an instance of the book controller.
Controller = new BookStoreController();
}
//Create a test method.
[Test]
public void ExpectedToReturnABook()
{
//call the get book method on the controller passing in an isbn.
string isbn = "9780672329487";
Book WCFUnleashed = Controller.GetBook(isbn);
//test that the book return is not null.
Assert.IsNotNull(WCFUnleashed);
}
}
}
Now let’s build the application. If you’re familiar with programming in .NET using VisualStudio, you’re probably thinking to yourself, this thing will not build. You're correct, and that points out our first objective. When using the TDD approach, we first write out our expectations, then we write the code required to meet them. Therefore when we attempt to build our project, we get the following:
This is because we don’t have any of the referenced classes defined (created). Let’s get them created.
Right-Click on the BookStore project and Select “Add” > “New Item” and choose “Class”.
Enter “Book.cs” as the name.
Follow the same steps and create a file named “BookStoreController.cs”.
Then Right-Click on the BookStore project and Click "Build".
Your BookStoreControllerTests project should resemble the figure below. The BookStoreController and Book classes have now been recognized as indicated by the code coloring.
Now try and build the project again by right-clicking on “Solution ‘BookStore’ (2 projects)” and clicking “Build”. Again you should receive an error. This is because, although we have created our classes, the BookStoreController.cs file does not contain a method called GetBook that accepts a string argument. So let’s fix this by adding the method below to the BookStoreController.cs file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BookStore
{
public class BookStoreController
{
//Get a book from the store by isbn
public Book GetBook(string isbn)
{
//Create a book instance and set it to a null object.
Book RequestedBook = null;
//Return the book
return RequestedBook;
}
}
}
Since OOP principles are beyond the scope of this tutorial, we will go with some basic implementations just to get our projects to build, and our unit tests to pass. There is one change to the class files that I should point out, and that is the use of the public keyword. The public keyword indicates that a class is accessible outside of its current assembly (dll) or project however you would like to look at it. By default, VisualStudio does not add the public keyword to newly created classes. So for each of the classes that were created, Book.cs and BookStoreController.cs,
Change:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BookStore
{
class BookStoreController
{
To read:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BookStore
{
public class BookStoreController
{
Now with our GetBook() method in place, lets attempt to build our project again.
Right-Click on the BookControllerTests project and select “Build”.
If you have followed along successfully, your Visual Studio Output window should display the text below, with an emphasis on the statement indicating that the build of our 2 projects succeeded.
------ Build started: Project: BookStore, Configuration: Debug Any CPU ------
BookStore -> C:\Users\Bacardi\Documents\Visual Studio 2008\Projects\BookStore\BookStore\bin\Debug\BookStore.dll
------ Build started: Project: BookStoreTests, Configuration: Debug Any CPU ------
BookStoreTests -> C:\Users\Bacardi\Documents\Visual Studio 2008\Projects\BookStore\BookStoreTests\bin\Debug\BookStoreTests.dll
========== Build: 2 succeeded or up-to-date, 0 failed, 0 skipped ==========
Now for the reason that we went through all of this, THE TEST!
At this point let’s launch NUnit and run the tests just as we did in the first lesson of this series.
From the NUnit application, Select “File” > “Open Project” and navigate to the BookStore project in the Visual Studio > Projects directory. Since we are testing our BookStoreTests assembly, we will navigate to the BookStoreTests/Bin/Debug folder and select the BookStoreTests.dll file.
For a normal installation on Windows Vista, the directory path would be:
C:\Users\[username]\Documents\Visual Studio 2008\Projects\BookStore\BookStoreTests\bin\Debug
You should see your project appear in the NUnit Gui’s left panel and then click “RUN” to run the test. If you have followed along successfully you should see the following:
Just as we should have suspected, our test failed. This is because we explicitly set our book object to a null object in the BookStoreController’s GetBook() method, and then we ran a test in the BookControllerTests' ExpectedToReturnABook() method that tested that was to ensure that the book was not null.
So again in TDD, we want to specify our expectations first, then write the code that fulfills ONLY THAT expectation. There are some theories on efficiency and the like surrounding this, but again that is beyond the scope here. Therefore, in sticking with the program, lets get the test to pass.
In order to do so, we need for our BookStoreController to return a book object that is not null, and the code below accomplishes this. So replace the existing code in your BookStoreController.cs file with this modified code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BookStore
{
public class BookStoreController
{
//Get a book from the store by isbn
public Book GetBook(string isbn)
{
//Create a book instance.
Book RequestedBook = new Book();
//Set the properties for the book. For this tutorial
//lets pretend that the controller actually pulled book details
//from the database, xml repository, or a webservice.
//One approach is to call a service that returns the requested book
//overriding the null on created above. So that might look like:
//
// try{
// RequestedBook = _bookService.getBook(isbn);
// }
//
//Where _bookService represents a service layer that handles calls to
//a repository of some type, hosted somewhere.
RequestedBook.Isbn = isbn;
RequestedBook.Title = "Windows Communication Foundation Unleashed";
//Return the book
return RequestedBook;
}
}
}
Also, we need our Book object to actually have the properties that we are setting in our BookStoreController.cs file defined. So modify your Book.cs file to resemble the code below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BookStore
{
public class Book
{
private string _isbn;
private string _title;
public string Isbn
{
get { return this._isbn; }
set { this._isbn = value; }
}
public string Title
{
get { return this._title; }
set { this._title = value; }
}
}
}
Build the solution again.
Go to NUnit and Click “RUN”, and as the French would say it “voila” our test has passed.
And that’s a wrap. In this very detailed part of the series we created a class library project, created a unit testing project, created a new test, wrote the code necessary to build the solution, and finally wrote the code necessary to pass the test.
As basic as it is, I hope this helps you conceptualize test driven development. Future tutorials in this series will not be as in-depth, as I would assume that the fundamentals of tests and how they work are understood. So tune back for the tutorials on the RhinoMocks and MoQ testing frameworks.
The series:
- Downloading and Installing NUnit
- Creating An Application, the TDD Approach
- TDD, Mocking with RhinoMocks[coming soon]
- TDD, Mocking with MoQ [coming soon]
Happy coding!
Comments
Comments are closed