2.4. Unit Testing Frameworks

The .NET Framework is lucky enough to have a vast number of excellent unit testing frameworks to support you when writing tests. Each framework has a different approach, targeting a different set of users. Choosing which unit testing framework to use comes down to experience and your own goals. Currently, the most common framework is nUnit and this is an excellent starting point.

Another popular testing framework is MbUnit, which focuses on extendibility and data-driven tests while being compatible with the nUnit syntax.

MSTest is the framework included as part of Visual Studio 2005 and 2008 and has slightly different syntax to nUnit. Instead of TestFixture attribute, it is called TestClass and instead of the Test attribute you need to use TestMethod.

Finally, the newest framework is xUnit.NET. xUnit is a very effective framework, which takes a different approach and view to the other frameworks. The syntax is also slightly different; for example you don't need to specify the TestFixture attribute, and instead of specifying the Test attribute, you use the Fact attribute. The reason for this name change is that both Brad Wilson and James Newkirk, the creators of xUnit, feel that unit tests should actually document "facts" about the code. As such, if the unit test fails, then the fact is wrong. Although this is a different view, it is actually logical. The reason for the name change is because the creators do not view each method as a test for the code; instead they view each method as a fact about the code.

When it comes to choosing a framework, if creating tests through code is a new concept, it's recommended that you start with nUnit. nUnit provides a great foundation for learning how to create tests. After you gain some experience with nUnit, start to investigate MbUnit or xUnit. Your initial experience with nUnit will provide a great basis to serve as a comparison to MbUnit and xUnit to see what those frameworks provide.

2.4.1. Unit Test Runners

After you have selected your framework you will need a runner to execute the tests. nUnit, as with MbUnit and xUnit, comes with a console application for executing tests from the command line, with the results being written to the console. It also includes a UI and support for executing your tests from a build script such as NAnt or MSBuild.

Visual Studio includes support for executing tests created with MSTest, and in Visual Studio 2010 this is being opened to other frameworks. There is also a commercial add-in called TestDriven.NET which provides Visual Studio integration allowing you to execute tests for nUnit, MbUnit, and xUnit.

Finally, a framework called Gallio is providing a test automation platform offering a runner, including GUI, command line, TD.NET, Visual Studio integration, and many others.

2.4.2. Test Driven Development

We, along with many other people, feel that unit testing only solves a small part of the problem of testing. Unit testing provides benefits in the fact that you have some verification that the software is working as expected going forward. However, there is one major problem. Adding unit tests to a system after it has been developed is a long process because you are attempting to reverse-engineer the system's implementation. When the code is already in place, then your unit tests' implementation are bound to how the code was written and where the code hasn't been considered for testing. As such, the writing of the tests can be much more difficult and harder to understand.

As a developer, your motivational mindset is also different. You have already written the code, which means taking the additional step of writing the code to verify it works is more difficult to justify. Developers can be more tempted to bypass this stage with the promise of going back and adding unit tests at a later point. Sadly, this is a slippery slope that many developers have been faced with. The more code that is added without unit tests, the harder it will be to go back and add unit tests, until you reach the point where both the code coverage and your confidence in the unit tests are much lower. During this anti-pattern process, all the different considerations that once would have been taken (such as running the tests and designing for testability) have been forgotten, with the code quality deteriorating.

To combat this, Test Driven Development (TDD) is used. The concept of TDD can definitely help combat the concerns mentioned previously. Instead of writing the unit tests after you have finished development, referred to as Test After Development (TAD) or Test Eventually Development (TED), you write the unit test first and then implement code to make the test pass. After doing this, you can improve the code to improve maintainability. The TDD community came up with a mantra for this approach that people should follow, called "Red, Green, Refactor."

The red is a reference for creating a new test that will fail when it is executed because the code hasn't been implemented. This first part is a bit obvious on the .NET platform: because the code hasn't been implemented, the application will not compile — hence your first phase of red. The red reference is the first stage of the test. You have written a test based on how you are planning to implement a small section of code. Generally, you the developer, only write one test based on the code you are just about to implement, ideally within the same minute to help you maintain focus. The test should set up the object you are going to interact with, the method you are going to call, and the expected result at the end. If you don't know what the expected result is before you start, it's important to take a step back and think about your code and its requirements. If you start writing code without a clear objective, then it is likely to miss your actual requirement. This is one of the advantages that TDD brings, encouraging you to focus on what is actually happening and how to get it done.

After you have the first stage of the test failing, the next stage is to make it green. The most important fact to remember is that you should only write enough code to make the test pass, nothing more and nothing less. Another point to remember is that you should only write new code if you have an associated failing test. If you need to make a change, then you should write a failing test which demonstrates the change you are about to make and execute the test to verify the change has been done correctly.

Identifying this change from failing to passing is important. First, it tells you that you are finished — if you need to continue, you need another test. Secondly, it comes back to having confidence in your tests. If you only ever see the test as green, how can you be 100 percent sure that it is working correctly and you haven't made a mistake? By seeing it change when you expect, then you have more confidence in both your test and your implementation. Again, this is why the red stage is important, because you want to see the test red (a test failure) before you proceed.

After you have your code implemented and it passes the test, the next stage is to refactor. Refactoring is the concept of taking your existing code and improving the quality. At this stage you are not adding or changing any functionality, but you are trying to improve the readability and maintainability of the code going forward. Common scenarios of refactoring are extracting code into a separate method to remove duplication, or moving existing methods into a separate class to improve the isolation. By completing the refactoring at this stage, you are still focused on the small isolated section of code you have just implemented, but you can also take a step back and look at how it fits into the overall picture of your systems architecture. Although you shouldn't be changing the functionality of the code, you have your tests in place to ensure you don't break anything.

Refactoring is the process of improving code, but without changing the result that the code produces. If you have ever split large functions into smaller functions, or have renamed a class, then you have refactored code. As with many other computer science techniques and practices, you may have been practicing this process but been unaware that there was a name for it. The term re-factor can be traced to William Opdyke's 1993 Ph.D. dissertation entitled Refactoring Object-Oriented Frameworks.

The practice of refactoring plays a large role in Test Driven Development (TDD), which we cover in depth in Chapter 3.

After you have implemented a section of code following the Red-Green-Refactor rule, the next step is to repeat it. By following this approach for your entire development process, you will be well on your way to successfully performing TDD.

The problem with following this approach is that it takes a great deal of self-control and willpower, at least at the beginning. As mentioned before, the best approach is to get yourself into the mindset of wanting to develop your code using TDD. If you get into the habit of always wanting to write a test before you write your code, you stand a much better chance of succeeding. After you have been in this mindset for a short period of time, you'll find that it becomes a very natural way to develop software.

You will also find that if you are developing software using TDD, the actual code you write will be very different. Because you are using the tests to guide you, you will find that you are writing much less code because you are only writing exactly what is required. Although this is important, a more important goal is the design of the system. This brings us to an important fact about TDD that is often overlooked. TDD is not about testing! TDD is about designing your software. The tests that are created are just a happy side effect that can be used for regression testing in the future.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.179.220