Write your own blog engine, part 1
Introduction
I've decided that I need to write another book about TDD programming. The project I've chosen is a blogging engine, per Rob Conery's suggestion - it should be simple enough that I don't get bogged down in details, but complex enough not to be perceived as a toy object. What is the minimal feature set? I've decided to start with these features:- Display the blog title
- Display the most recent five posts - date, title and content
- Login (especially since it's required by the next features)
- Add a new post
- Delete an existing post
- All dependencies between classes will be passed in constructors
- All dependencies that are services ("injectables", in Misko Hevery's terminology) will be exposed as interfaces
[TestClass]
public class AcceptanceTests
{
private const string BASE_URL = "http://localhost:63516/";
[TestMethod]
public void HomePageHasCorrectTitle()
{
using (var web = new WebClient())
{
var html = web.DownloadString(BASE_URL);
var doc = new HtmlDocument();
doc.LoadHtml(html);
var title = doc.DocumentNode.SelectSingleNode("/html/head/title").InnerText;
Assert.AreEqual("MyBlogEngine", title);
}
}
}
I am using ReSharper; it makes my life easier. In particular, it tells me that I need to add a reference to System.Xml and the following using clauses:
public class AcceptanceTests
{
private const string BASE_URL = "http://localhost:63516/";
[TestMethod]
public void HomePageHasCorrectTitle()
{
using (var web = new WebClient())
{
var html = web.DownloadString(BASE_URL);
var doc = new HtmlDocument();
doc.LoadHtml(html);
var title = doc.DocumentNode.SelectSingleNode("/html/head/title").InnerText;
Assert.AreEqual("MyBlogEngine", title);
}
}
}
using System.Net;
using HtmlAgilityPack;
using Microsoft.VisualStudio.TestTools.UnitTesting;
I will not specify the required usings each time; I hope that won't be too big of a problem.
Press F5 to launch the MVC application but then go back to Visual Studio and stop the debugger. Also, the "63516" port shown above is the port my Visual Studio decided to listen to; either change yours to match or change the code.
Ok, time to run the test. You can use Ctrl-R, A to run all the tests in the solution or you can click on the corresponding button in the tests toolbar. The test fails with the error "The remote server returned an error: (404) Not Found." This is good - I haven't created the home controller yet so it should error out. I should fix this, but I don't want to write production code without unit tests to guard it.
Unit tests, in contrast to acceptance tests:
using HtmlAgilityPack;
using Microsoft.VisualStudio.TestTools.UnitTesting;
- Only test a small portion of the code (usually a method)
- Do not access the network, the database or really anything except the class being tested.
[TestClass]
public class HomeControllerTests
{
}
The first test should make sure that the "/" page returns something (note that you will need to add a reference to System.Web.Mvc from the test project):
public class HomeControllerTests
{
}
[TestMethod]
public void IndexReturnsView()
{
var sut = new HomeController();
var result = sut.Index() as ViewResult;
Assert.IsNotNull(result);
}
In order to make the project compile I'll need to add the controller. Right-click on the Controllers folder in the MyBlogEngine.MVC project and select Add / Controller. Type "Home" (so that the name of the controller is HomeController) and choose the "Empty Controller" template. Delete the extra crud that Visual Studio insists on adding and leave the class looking like this:
public void IndexReturnsView()
{
var sut = new HomeController();
var result = sut.Index() as ViewResult;
Assert.IsNotNull(result);
}
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Now go back to the unit test, add the required using clause to make it pass and run the tests again. Note that the unit test passes (the method does return a view) but the acceptance test fails, this time with "The remote server returned an error: (500) Internal Server Error." That's because when the application tried to render the view, it couldn't find it. Fixing that problem is easy: create a new folder under Views (called Home) and then add a new Index view to it. Leave the contents of the view unchanged and re-run the tests.
The acceptance test is still failing but this time with a much better error message: "Expected:<MyBlogEngine>. Actual:<Index>." All that remains to be done is to fix this; change the contents of the Index view to:
{
public ActionResult Index()
{
return View();
}
}
@{
ViewBag.Title = "MyBlogEngine";
}
<h2>@ViewBag.Title</h2>
This time all the tests pass. We've reached the first milestone!
ViewBag.Title = "MyBlogEngine";
}
<h2>@ViewBag.Title</h2>
Comments