In the following five chapters, we build the beginnings of a real-world ASP.NET MVC application—we build a simple blog application. I was motivated to build the blog application to satisfy two goals.
First, I want to demonstrate how you can use the features of the ASP.NET MVC framework in the context of building a real-world application. In the previous chapters of this book, we focused on particular features of the ASP.NET MVC framework. In the following chapters, you learn how all these features work together.
Second, I want to illustrate and promote a particular design methodology for building software applications called test-driven development. One of the primary goals of the ASP.NET MVC framework is to make it easy to practice test-driven development when building ASP.NET applications.
In this chapter, I provide you with a brief introduction to test-driven development. In particular, I explain why you might consider using test-driven development when building your own applications.
When practicing test-driven development, you develop an application by performing these steps over and over again:
This process is called Red/Green/Refactor because a unit testing framework displays a red bar for a failing test and a green bar for a passing test.
When practicing test-driven development, the first step is always to create a failing test. You use the test to express how you want your code to behave.
For example, in our blog application, we start by writing a test that verifies whether you can retrieve a list of blog entries. We create this test before we write any application code.
After you create a failing test, you can allow yourself to write application code. However, you should allow yourself to write only enough code to cause the test to pass (to go green).
Finally, every once in a while, you take a step back from your code and consider how to improve the overall design of the code. You consider how you can refactor your code to have a better design. Because you have the safety net of tests, you can fearlessly refactor your code. Your tests immediately tell you if you have broken existing functionality.
Test-driven development is, first and foremost, a software design methodology. The primary goal of test-driven development is not to create a well-tested application. Instead, the goal is to create a well-designed application.
A well-designed software application is an application that can be easily maintained and extended over time. The claim is that test-driven development leads to better designed applications because the methodology forces you to continuously focus on design.
The word test in test-driven development is misleading. Many people have suggested that the word test should be dropped from the name of test-driven development because the word confuses so many people about the goals of test-driven development. For example, proponents of behavior-driven development (a spin-off of test-driven development) avoid using the word test. Instead, they use the word specification in place of the word test. See the entry on behavior-driven development at Wikipedia.org (http://en.wikipedia.org/wiki/Behavior_driven_development).
Other people who practice test-driven development, on the other hand, are happy with the word test.
Test-driven development enforces something called the KISS Principle (Keep It Simple Stupid) and the YAGNI Principle (You Ain’t Gonna Need It). After you write a failing test, you should allow yourself to write only enough application code to pass the test and nothing more.
A developer feels the constant temptation to write more code than is needed in the current situation. I’ve been involved in many projects in which a team of developers developed a vast library of functions that ended up never being used. These giant libraries are a sad waste of many hundreds of hours of developer time.
Test-driven development forces you to concentrate on what you need to write to satisfy the requirements of the customer. You can think of a test as a mini-specification. By creating a criterion of success upfront that is expressed with a test, you can prevent yourself from wandering into the woods and writing code that no one will ever use.
Test-driven development grew out of agile development, which grew out of a reaction to waterfall development. One important goal of test-driven development is what Kent Beck calls incremental design and what Martin Fowler calls evolutionary design. Instead of designing an application all at once and upfront, an application is designed incrementally test-by-test.
Waterfall design is modeled on the disciplines of electrical engineering or civil engineering. In civil engineering, there is a sharp distinction between the architect of a bridge and the workers who actually build the bridge. A highly educated and highly paid architect prepares a comprehensive design for a bridge. Only after the design is completed do the low-paid workers go out and start building the bridge brick-by-brick or girder-by-girder.
Waterfall development in software follows the same model. A highly paid software architect creates a comprehensive architectural design for a software application and then hands the design off to a team of lower-paid developers who implement the design. There is a sharp separation between the process of designing an application and the process of developing the application.
Proponents of test-driven development reject this analogy between software engineering and civil engineering. Instead, proponents of test-driven development claim that the software is the design. In other words, every developer is an architect and every developer should constantly be thinking about design. The developer is responsible for designing the application, and the compiler is responsible for building the application.
Test-driven development makes design central to the process of building software. Instead of trying to design an entire application upfront, proponents of test-driven development advocate that every developer participates in the continuous design of an application.
Test-driven development sharply separates the process of developing software into three separate stages. You have a different perspective on your code during each stage.
When you write a test, you are actively designing your code by taking the perspective of someone who will use the code. Creating a test forces you to think about the best way to build the public interface for your application.
After you write a test, you shift perspectives. You move from the perspective of someone who is designing the software to someone who is implementing the software. You shift from being a highly paid architect to a lowly paid laborer.
And, finally, in the refactor stage, you take a more global perspective on your application. During the refactor stage, you concentrate on improving the overall design of your application instead of an individual part.
It is common to use the expression unit test for the type of test used in the context of test-driven development. Strictly speaking, however, the tests that you write within the context of test-driven development are related to unit tests, but not the same. Although you use a Unit Testing Framework, such as NUnit or Visual Studio Test, to build both TDD tests and unit tests, TDD tests have a very different purpose than unit tests.
Creating unit tests for an application is valuable. You can use unit tests to determine whether an application behaves in the way that you intend it to behave. Because a unit test tests a unit of code in isolation, you can use unit tests to quickly find defects in your code. In other words, unit tests are extremely useful in regression testing.
In contrast to unit tests, you do not create TDD tests to test your application. Instead, you use TDD tests to drive the development and design of your application. TDD tests work like mini-specifications or mini-acceptance tests.
Kent Beck, in his book Test-Driven Development by Example, is careful to refer to the tests that he uses in test-driven development as “small-scale tests” instead of unit tests because “they don’t match the accepted definition of unit tests very well.” Again, it would be more accurate to think of tests used in the context of test-driven development more as mini-specifications than unit tests.
When practicing agile development, you always start with a list of user stories. The user stories are nontechnical descriptions of the ways in which your customers want to interact with an application. Typically, a user story consists of no more than two or three sentences.
When developing our blog application, we always start with a list of user stories written on a napkin. We use the user stories to create the initial set of tests. The tests are intended to capture the intentions behind the user stories. In other words, the tests work as (mini) customer acceptance tests.
User stories are written for the benefit of customers. The tests, in contrast, are written for the benefit of the developer. We use the tests to concentrate on the next task that needs to be accomplished. The tests act as automated success indicators.
There are several Unit Testing Frameworks that you can use when building an ASP.NET MVC application. The most popular Unit Testing Framework in the Microsoft .NET world is NUnit. The NUnit framework is maintained by Charlie Poole. You can use NUnit with both Microsoft Visual Studio and Microsoft Visual Web Developer.
In the following chapters, we use the built-in Unit Testing Framework included with Visual Studio called Visual Studio Test. To use this framework, you need the full version of Visual Studio. If you use Visual Web Developer, I recommend that you use NUnit, which you can download for free from the following location: http://NUnit.org.
Test-driven development is a controversial topic. Within the test-driven development movement, there are many conflicting voices. I recommend that you read the source material. Here are some of the resources that I have found particularly valuable:
• Test-Driven Development by Example by Kent Beck. This book is the foundational book for test-driven development. It includes an extensive walkthrough of building a money converter library through test-driven development.
• What Is Software Engineering? by Jack Reeves (www.bleading-edge.com/Publications/C++Journal/Cpjour2.htm). This article is one of the foundational articles of the agile movement. In this article, Jack Reeves claims that all developers are architects.
• Working Effectively with Legacy Code by Michael Feathers. This book focuses on using test-driven development with code that is not covered by tests. The title of this book is misleading. This is an excellent book on the topic of test-driven development in general.
• Agile Principles, Patterns, and Practices in C# by Robert and Micah Martin. This is a great book, which discusses test-driven development and addresses several topics beyond test-driven development.
• Is Design Dead? by Martin Fowler (http://martinfowler.com/articles/designDead.html). In this article, Martin Fowler advocates evolutionary design. He argues that design is not dead and that design is compatible with test-driven development.
• Mocks Aren’t Stubs by Martin Fowler (http://martinfowler.com/articles/mocksArentStubs.html). In this article, Martin Fowler distinguishes between state verification tests and behavior verification tests and distinguishes between classical test-driven development and Mockist test-driven development.
• Test-Driven Development in Microsoft .NET by James Newkirk and Alexei Vorontsov. Unfortunately, this book is getting a little dated because it was written for an earlier version of the .NET framework. However, it is still the best book for learning about test-driven development in the Microsoft world.
The purpose of this chapter was to introduce you to the goals of test-driven development. In this chapter, you learned that test-driven development is first and foremost an application design methodology. By practicing test-driven development, you can build applications that can better withstand the tests of time.
In the following chapters, you learn how to use test-driven development in the context of building a real-world application. We start with the first test and move on from there.
18.191.103.28