What is Unit testing: A unit is a piece of code or usually method that
invokes another piece of code and checks the correctness of some assumptions
afterward? If the assumptions turn out to be wrong, the unit test has failed. A
unit test is a method or function.
This definition is from Wikipedia
Property of Unit test: A unit test has the following properties
- It should be automated and repeatable.
- It should be easy to implement.
- Once written, it should remain for future
use.
- Anyone should be able to run it.
- It should run quickly.
Many of us get confused with the act of testing
of software with the concept of a unit test. To start off, ask yourself the
following questions about the tests you've written up to now:
- Can I run and get results from a unit test
I wrote two weeks or months or years ago?
- Can any member of my team run and get the
results from unit tests I wrote two months ago?
- Can I run all the unit tests I've written
in no more than a few minutes?
- Can I run all the unit tests I've written
at the push of a button?
- Can I write a basic unit test in no more
than a few minutes?
If you've answered "no" to any of these
questions, there's a high probability that what you're implementing isn't a unit
test. It's definitely some kind of test, and it's as important as a unit test,
but it has drawbacks compared to tests that would let you answer "yes" to all of
those questions.
A simple unit test example: It's possible to write an automated unit test
without using a test framework. In this section, I'll show what writing such a
test without a framework can look like, so that when I later in article
demonstrate, you can contrast this with using a framework.
Assume we have a SimpleParser class that we'd like to test. It has a method
named ParseAndSum that takes in a string of 0 or more comma-separated numbers.
If there are no numbers, it returns 0. If there's a single number, it returns
that number as an int. If there are multiple numbers, it adds them all up and
returns the sum (although, right now, the code can only handle 0 or 1 number).
public class SimpleParser
{
public int
ParseAndSum(string numbers)
{
if (numbers.Length == 0)
{
return 0;
}
if (!numbers.Contains(","))
{
return int.Parse(numbers);
}
else
{
throw new InvalidOperationException(
"I can only handle 0 or 1 numbers
for now!");
}
}
}
We can create a simple console application project that has a reference to the
assembly containing this class, and we can write a SimpleParserTests method as
shown in below code. The test method invokes the production class (the class to
be tested) and then checks the returned value. If it's not what's expected, it
writes to the console. It also catches any exception and writes it to the
console
public class SimpleParserTests
{
public static void TestReturnsZeroWhenEmptyString()
{
try
{
SimpleParser p = new
SimpleParser();
int result = p.ParseAndSum(string.Empty);
if (result != 0)
{
}
Console.WriteLine(
@"***SimpleParserTests.TestReturnsZeroWhenEmptyString:
-------
Parse and sum should have returned 0 on an empty string");
}
catch (Exception
e)
{
Console.WriteLine(e);
}
Next, we can invoke the tests we've written by using a simple Main method run
inside a console application in this project, as seen in code below. The Main
method is used here as a simple test runner, which invokes the tests one by one,
letting them write out to the console. Because it's an executable, this can be
run without human intervention (assuming the tests don't pop up any interactive
user dialogs).
public static void Main(string[] args)
{
try
{
SimpleParserTests.TestReturnsZeroWhenEmptyString();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
It's the test method's
responsibility to catch any exceptions that occur and write them to the console,
so that they don't interfere with the running of subsequent methods. We can then
add more method calls into the Main method as we add more and more tests to the
project. Each test is responsible for writing the problem output (if there's a
problem) to the console screen.
Obviously, this is an ad hoc way of writing such a test. If you were writing
multiple tests like this, you might want to have a generic ShowProblem method
that all tests could use, which would format the errors consistently. You could
also add special helper methods that would help check on various things like
null objects, empty strings, and so on, so that you don't need to write the same
long lines of code in many tests.
The below code shows what this test would look like with a slightly more generic
ShowProblem method.
Using a more generic implementation of the ShowProblem method.
public class TestUtil
{
public static void ShowProblem(string test, string message)
{
string msg = string.Format(@"
---{0}---
{1}
--------------------
", test, message);
Console.WriteLine(msg);
}
}
public static void TestReturnsZeroWhenEmptyString()
{
//use .NET's reflection API to get the current
method's name it's possible to hard code this,but it's a useful technique to
know
string testName = MethodBase.GetCurrentMethod().Name;
try
{
SimpleParser p = new SimpleParser();
int result = p.ParseAndSum(string.Empty);
if(result!=0)
{
//Calling the helper method
TestUtil.ShowProblem(testName,
"Parse and sum should have returned
0 on an empty string");
}
}
catch (Exception e)
{
TestUtil.ShowProblem(testName, e.ToString());
}
}
Unit-testing frameworks can help make helper methods more generic like this, so
tests are written more easily.
What is NUnit: A new way to writing real-world unit tests with a
framework called NUnit-a .NET unit-testing framework. It's my favorite framework
in .NET for unit testing because it's easy to use, easy to remember, and has
lots of great features.
So before we start working with NUnit, we need to look at what a unit-testing
framework is, and at what it enables us to do that we couldn't and wouldn't have
done without it.
The points to be remembered about NUnit are listed below:
-
NUnit is not an automated GUI
testing tool.
-
It is not a scripting
language, all test are written in .NET supported language, e.g., C#, VC,
VB.NET, J#, etc.
Frameworks for unit testing:
Consider the advantages an integrated development environment (IDE) gives
you as a developer. Unit-testing frameworks offer similar advantages for
testing. When using a modern IDE like Visual Studio .NET or Eclipse for Java,
you do all your coding tasks within that Environment, in a structured manner.
You write the code, you compile it, you build any resources (like graphics and
text) into it, and you create the final binary-all that building and compiling
with no more than a couple of keystrokes. Doing things completely manually would
be error-prone and time-consuming, and people would defer doing that as much as
possible. These problems are alleviated by tooling. In the same way,
unit-testing frameworks help developers write tests more quickly with a set of
known APIs, execute those tests automatically, and review the results of those
Tests easily.
So now we are ready to start using NUnit framework for doing unit testing. I
have compiled this exercise in below steps.
Step 1: Download NUnit
1. Download NUnit from
http://www.nunit.org/index.php?p=download.
Download Window version i.e.
win NUnit-2.5.10.11092.msi
Step 2: Installing NUnit
To install NUnit, runs the setup program you downloaded. The installer Will
place a shortcut to the GUI part of the NUnit runner on your desktop, but the
main program files should reside in a directory named something like c:\Program
Files\NUnit-Net-2.5 .10.
Step 3: Loading up the solution
The NUnit GUI is divided into three main parts: the tree listing the tests on
the left, messages and errors at the top right and stack trace information at
the bottom right.
Step 4: Create a Window Console Project in Visual Studio
In program.cs write below code. We here in below code are adding and subtracting
two integers.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter
two numbers\n");
int iNumber1;
int iNumber2;
iNumber1 = int.Parse(Console.ReadLine());
iNumber2 = int.Parse(Console.ReadLine());
HelperClass helper = new
HelperClass();
int x = helper.Add(iNumber1, iNumber2);
Console.WriteLine("\nThe
sum of " + iNumber1 + " and " +
iNumber2 + " is " + x);
Console.ReadKey();
int y = helper.Subtract(iNumber1, iNumber2);
Console.WriteLine("\nThe
difference between " + iNumber1 + " and"
+ iNumber2 + " is " + y);
Console.ReadKey();
}
}
public class HelperClass
{
public HelperClass() { }
public int Add(int a, int b)
{
int x = a + b;
return x;
}
public int Subtract(int a, int b)
{
int x = a - b;
return x;
}
}
Step 5: Creating Test Project
-
Now to the solution of the
project, add a new class library project and name it followed by ".Test" (it
is the naming convention used for unit testing). Import the downloaded DLL
files into the project and follow the steps given below.
-
Adding NUnit reference in your
Visual Studio 2008 IDE.
-
In the newly added project,
add a class and name it as TestClass.cs.
-
Add nunit framework reference
in class.
using NUnit.Framework;
-
Add reference to your main
code project.
using NUnitProjectOne;
-
In the class file, write the
following code:
namespace
Nunit.Test
{
[TestFixture]
public class
TestClass
{
[TestCase]
public void
AddTest()
{
HelperClass helper =
new HelperClass();
int result = helper.Add(20, 10);
Assert.AreEqual(30, result);
}
[TestCase]
public void
SubtractTest()
{
//Arrange
HelperClass helper =
new HelperClass();
//Act
int
result = helper.Subtract(20, 10);
//Assert
Assert.AreEqual(10,
result);
}
}
}
Unit
test usually comprises three main actions:
Step 7: How to run the NUnit
GUI runner from within Visual Studio C# Express 2008
We can run NUnit from the Menu option it installed under [Start/Programs/NUnit]
but it is more convenient to do so from Visual Studio Express itself.
Visual Studio C# Express allows you to define outside "Tools" which can be run
from the menu. This is exactly what we would like to do with the NUnit GUI
Runner. The GUI Runner is a tool which loads our program and executes all the
unit tests it finds and reports the results in a separate window.
Click on "Tools > External Tools…" to add NUnit's GUI Runner.
The following definition allows us to run the tests in our current project:
Configuring Nuint to enable it to open within Visual Studio
To run the unit test, simply click "NUnit" from the Tools Menu.
Select NUint to run .
Nunit test result
Now lets change result value in TestClass and then see how NUnit tests it.
[TestFixture]
public class TestClass
{
[TestCase]
public void
AddTest()
{
HelperClass helper = new HelperClass();
int result = helper.Add(20, 10);
Assert.AreEqual(50, result);
}
[TestCase]
public void
SubtractTest()
{
HelperClass helper = new HelperClass();
int result = helper.Subtract(20,
10);
Assert.AreEqual(10, result);
}
}
Built this and run the NUnit. This time it show error for Add method.
Step 7: Understanding Terms
-
[TestFixture]
The [TestFixture] attribute denotes a class that holds automated NUnit
tests. (If you replace the word "Fixture" with "Class", it makes much more
sense.)
-
[TestCase]
The [Test] attribute can be put on a method to denote it as an automated
test to be invoked. Put this attribute on your new test method.
-
Assert
The Assert class has static methods and is located in the NUnit.Framework
namespace. It's the bridge between your code and the NUnit framework, and
its purpose is to declare that a specific assumption is Supposed to exist.
If the arguments that are passed into the Assert class turn out to be
different than what we're asserting, NUnit will realize the test has failed
and will alert us. We can optionally tell the Assert class what message to
alert us with if the assertion fails. The Assert class has many methods,
with the main one being Assert.IsTrue (some Boolean expression), which
verifies a Boolean Condition. But there are many other methods. This one
verifies that an expected object or value is the same as the actual one:
Assert.AreEqual(expectedObject, actualObject, message);
Here's an example:
Assert.AreEqual(2, 1+1, "Math is broken");
-
[Setup]:
Is generally used for any initialization purpose. Any codes that must be
initialized or set prior to executing a test are put in functions marked
with this attribute. As a consequence, it avoids the problem of code
repetition in each test.
-
[Ignore]:
This is the attribute which is used for the code that needs to be bypassed.
-
[ExceptionExpected]
This attribute is used to test methods that need to throw exception in some
cases. The cases may be FileNotFoundException and others.
-
[TearDown]:
This attribute denotes a method to be executed once after each test in your
class has executed.
Conclusion:
-
Its common practice to have
one test class per tested class, one test project per tested project, and at
least one test method per tested method.
-
Use the [SetUp] and [TearDown]
attributes to reuse code in your tests, such as code for creating and
initializing objects all your tests use.
Hope you enjoyed this ride about
unit testing and NUnit. Cheers