By Kamran M. Qamar
Usually, software testing is done at the end of the software development cycle and is relegated to a less creative testing department, hence making software testing a low priority in the development process. This approach results in error detection at the very end and sometimes even by the customer.
In reality testing is much easier than its reputation. As a matter of fact, testing is much fun because it results in better design and cleaner code. The time spent specifying test cases quickly pay off, you can define a series of test cases once and you can use them again and again. This means that regardless of looming deadlines, the developer always has a complete test suite at hand. This saves invaluable time when integrating new functions and brings a significant competitive advantage for both new and further development.
What Are Unit Tests?
A unit test is nothing more than the code wrapper around the application code that permits test tools to execute them for fail-pass conditions.
Why Should You Use Unit Tests?
Forget for a moment that there is something called XP (Extreme Programming) that coined the Unit Test term. The most of the projects developed today are always under tight development schedules and usually have only its developers as the tester of their code. By writing the unit tests themselves they can have a head start towards bug-free and quality code. One will argue that if the developer is writing all the unit tests, it is quite possible to get the set of unit tests that are passable, because these unit tests are developed based either on the foreknowledge of application code or the assumptions made in the application code. However, do not be fooled with this, imagine what will happen if developer decides to change the application, her old test cases will break. That will force her to either re-think her changes or re-write the unit tests. The application architect or analyst can write all the unit test cases upfront (Not what XP recommend, but we are not worried about it) and test the developed code against these cases and functionalities. The advantage is well defined deliverable for the developer and more quantifiable progress. A developer can also use this to disciple their work habits e.g. she can write a set of unit test that she wants to accomplish in a days work. Once tests ready, she can start developing the application and check her progress against the unit test. Now she has a meter to check her progress.
What is the NUnit Framework?
NUnit framework is port of JUnit framework from java and Extreme Programming (XP). This is an open source product. You can download it from http://www.nunit.org. The NUnit framework is developed from ground up to make use of .NET framework functionalities. It uses an Attribute based programming model. It loads test assemblies in separate application domain hence we can test an application without restarting the NUnit test tools. The NUnit further watches a file/assembly change events and reload it as soon as they are changed. With these features in hand a developer can perform develop and test cycles sides by side. Before we dig deeper, we should understand what NUnit Framework is not:
Implementing the Test
You can write the test anywhere you like, for example:
I recommend implementing all the tests in the separate assembly because a unit Implementing unit test within the main assembly not only bloats the actual code, it will also create additional dependencies to NUnit.Framework. Secondly, in a multi-team environment, a separate unit test assembly provides the ease of addition and management. A standard naming convention will also help in further developing the test suite library for your application. We will discuss this in detail in coming section. For now, let create our first test assembly. Consider we want to write a simple Calculator class with four methods, which take two operands and perform basic arithmetic operations like addition, subtraction, multiplication and division. The code below defines the skeleton of a typical test class. using System; using NUnit.Framework; namespace UnitTestApplication.UnitTests { [TestFixture()] publicclass Calculator_UnitTest { private UnitTestApplication.Calculator calculator = new Calculator(); [SetUp()] publicvoid Init() { // // } [TearDown()] publicvoid Clean() { // } [Test] publicvoid Test() { } } } Things to note are: or Public Sub [MethodName] End Sub Now that we know the skeleton of a test class, lets look at a typical test method: [Test] publicvoid Test_Add() { int result Assertion.AssertEquals(4, result); } The key line to note is Assertion.AssertEquals(4, result); Assertions are the way to test for fail-pass test. NUnit framework support following assertions: Assert() AssertEquals() AssertNotNull() AssertNotNull() AssertNull() AssertSame() Fail() You can use as many Assert statements in a method as you like. However, NUnit framework, will show a method as failed if even a single assertion fails, as expected. But what is important to remember is that if first assertion fails, next assertion will not be evaluated, hence you will have no knowledge about next assertion. Therefore it is recommended that there should only be one Assertion statement per test method. If you believe there should be more than one statement, create a separate test case method.
test is related to quality assurance of the product, a separate aspect.
some code here, that need to be run
at the start of every test case.
code that will be called after each Test case
public void [MethodName](){}
= calculator.Add(2, 2);
What Should Be Tested?
This is a common and valid question. Typical test cases are:
The code below shows a typical boundary condition test for our Calculator case: [Test] [ExpectedException(typeof(DivideByZeroException))] publicvoid Test_DivideByZero() { int result = calculator.Divide(1, 0); } The key line of code to note here is; ExpectedExceptionAttribute and the way boundary conditions are checked. Well this seems quite obvious because .NET Framework takes care of it for us, but point here is to see how to implement. We can use the same technique to test boundary conditions for our methods. We should always write a failure test case e.g. Consider the following test case: [Test] publicvoid Test_AddFailure() { int result Assertion.Assert(result != 1); } It seems like a stupid case, but look at the following implementation of Add method: publicint Add(int a, int b) { return a/b; } Quite an obvious error, possibly a TYPO, but remember, there is no room for typos in coding.
= calculator.Add(2, 2);
Some Tips and Tricks
Using VS Debugger with NUnit Framework NUnit Framework, load and run the assemblies in separate AppDomain therefore you cannot use the debugging features of Visual Studio. However, you can attach any external program to VS to consume an assembly. Follow these steps: This property is set per project and persisted in project file. You will not get Star Application property as writeable. You need to press “Apply” button after setting Debug Mode property.
See the figure below:
With this you can run and debug your application code with great features provided by the Visual Studio. Using Configuration Files: Using Configuration files with NUnit is a tricky business. However, once you know where to put the configuration file, it is a piece of cake. Remember, NUnit creates a separate AppDomain to load the test case assembly i.e. our configuration file should be in the working folder of this AppDomain and its name should be [TestCaseAssemblyName].dll.config. For a typical scenario, this folder will be Test case assembly project’s subfolder named “bin”. Once this knowledge in hand, you can pass al your configuration settings in this configuration file and every thing will work like magic. Here is my demo configuration file and associated test case: <?xmlversion="1.0"encoding="utf-8"?> <configuration> <appSettings> <addkey="test"value="MyTest"/> </appSettings> </configuration> and Test Case: [Test] publicvoid Test_Configuration() { string test = System.Configuration.ConfigurationSettings.AppSettings["test"]; Console.WriteLine(test); Assertion.AssertEquals(test, "MyTest"); } On a side note, look at this line of code Console.WriteLine(test); The nunit-gui.exe is a smart GUI that capture all the console output and present them in its “on tab frame”. You can use this simple trick for quick debugging. Clean Cache The NUnit Framework caches the test case assembly information at “C:\Documents and Settings\
Figure 1
Use Factory Pattern
Use factory pattern to define tests that uses different input values for same functionalities.
The Naming Convention and Standards
- As discussed above all test cases should go in a separate assembly. A suggested name for such assembly is [CodeAssembly].UnitTests.dll e.g.
UnitTestApplication.UnitTests.dll
- You should follow the naming rule defined in .NET Framework SDK. A good tool to use is FxCop to force the naming convention.
- This assembly should have at minimum one-to-one relation between methods and test methods.
- Test case name should be Test_[MethodToBeTested][SomeAttribute] e.g.
Test_Add,
Test_AddFailure.
I hope that with this information in hand you will be able to write better test cases, test fixtures and test suites that will result in not only your productivity increase, but will also create a discipline to produce bug less and efficient code.
Happy Programming!