Quantcast
Channel: Programming Tidbits - Unit Testing
Viewing all articles
Browse latest Browse all 2

Getting Started With Unit Testing

$
0
0

I consider myself as a big advocate of the unit testing paradigm. I think that projects that incorporate unit testing as part of the development cycle will grow to be more stable and easier to maintain. In this post I want to give a jump start to all of you who still don't write unit tests, or have not yet made up their mind about the issue.

Why do we need unit tests?

Consider the following scenario, one of your team mates developed a caching framework that is widely used within your team. This team mate is long gone and it's up to you to maintain this framework. A new project is about to use this framework in a multi-threaded environment and you've got a new assignment to turn this framework to be thread safe. It didn’t take you long to finish the assignment and send it to QA, and everything works great! Well, almost everything. Every now and then the application crashes! What do you do? What is the cause of the crash? Was it always there, or is it because of your changes? The only way for you to know, is to debug the application.

Now, let's move to a parallel universe in which the original developer of this framework took the time and wrote extensive unit tests for it. Now, when you receive the assignment, your work flow will be:

  1. Before making any changes, make sure that all the tests pass.
  2. Implement your changes.
  3. Write additional unit tests in order to test the added functionality.
  4. Run the entire test suite and insure that all the tests pass.

If you're into TDD, you'll switch between steps 2 and 3. But that's a subject for a different post.

As you can see, the unit tests serves as our safety net. Having a quick and reliable way to test the correctness of the code allows us to be more confident about the code we add or change. If one of the original tests fails after adding our own code, we can know for sure that it's indeed our code that caused it to fail. Furthermore, because the tests are very specific, we can instantly know where to look for the bug.

Of course, there is always the possibility that the coverage of the tests is not wide enough, and a sneaky bug passed our iron shield. In that case, after debugging and finding the root cause, we will just add another test to check that specific issue, insuring that no more bugs will sneak through that particular hole.

To sum up, writing unit tests has the following advantages:

  • Gives us confidence to change an existing code. As long as the tests pass, we're OK.
  • Serves as a reference for the right usage of our API.
  • Constitutes a statement of what the code is intended to do and what it is not.
  • Gives us the chance of looking at the code from the eyes of its users. Not once, after writing unit tests for an API, I decided to change it because it was not intuitive enough to use.

Where do we place our unit tests?

As I said before, I think that the unit tests should be completely separated from the rest of our code. I prefer creating a separate testing project for each of our tested projects and place it in the same solution. The advantages of this method are:

  • Separation between production code and testing code. The purpose of each line of code is very clear.
  • The deployed application will not contain our testing code. That way we're not revealing things that we don’t want to, and our assemblies are lighter.
  • The deployed code will not be dependent on the unit test framework.
  • Placing the tests projects within the same solution as the tested project simplifies the navigation between them.

Regarding the naming conventions, I prefer calling the tests project in the same name as the tested project, only with a 'Tests' suffix. For example, the tests project for MyProject will be named MyProject.Tests.

How do we write our unit tests?

After the why and the where, it's time to talk about the how. Entire books were written about how to write unit tests. My intention is just to write about the minimum that is required in order to get you started.

Writing unit tests should not stand in our way, it should be quick and easy. To make sure that we will concentrate on the test code only, without having to deal with all the boilerplate code, we will use a unit testing framework.

More than one framework exist in the .NET ecosystem. In this post I will focus on NUnit. The following skeleton describes a typical usage:

using System;
using NUnit.Framework;

namespace CachingFramework.Tests
{
	[TestFixture]
	public class CacheTests
	{
		[TestFixtureSetUp]
		public void TestFixtureSetUp()
		{
			// Things to do once, before we start our tests
		}

		[TestFixtureTearDown]
		public void TestFixtureTearDown()
		{
			// Things to do once, after all the tests are finished
		}

		[SetUp]
		public void SetUp()
		{
			// Things to do before each test
		}

		[TearDown]
		public void TearDown()
		{
			// Things to do after each test
		}

		[Test]
		public void GetItem_WhenItemExistInCache_ItemIsReturned()
		{
			….
			// We use the Assert class to make assertions about the code
			Assert.IsNotNull(item);
		}

		[Test]
		[ExpectedException (typeof(ArgumentNullException)]
		public void GetItem_WithNullId_ThrowsArgumentNullException()
		{
			// We can verify that a certain exception is thrown by using the ExpectedException attribute
		}
	}
}

As you can see, NUnit let us concentrate on the tests themselves. In order to start writing our tests, we need to do the following:

  1. Create a new Class Library project for our tests.
  2. Add a reference to the tested project.
  3. Add a reference to the NUnit framework (nunit.framework.dll)
  4. Create a new class and decorate it with the [TestFixture] attribute. That way, NUnit will know that this class contains tests that need to be executed. I follow the practice of placing all the tests of a given class in one test fixture.
  5. Create a test methods and decorate them with the [Test] attribute. That way, NUnit will know to treat those methods as tests.
  6. As you can see in the skeleton above, by using the appropriate attributes, we can also instruct NUnit which methods to run before all the tests, before each test, after each test, and after all the tests.

After finishing writing the tests, we have to decide how to execute them. Here also, we have more than one option, I will describe two of them:

Using nunit-console or the NUnit GUI

NUnit comes with two applications that can execute our unit tests and produce a detailed report on which tests succeeded, which failed, and why.

In order to execute the unit tests using one of these applications we need to add its path to the Start External Program option in the tests project's properties, add the path to the tests assembly in the Command Line Arguments option, and press F5.

Using Resharper

Those of us that are using Resharper do not need to do anything special in order to run the unit tests, Resharper comes with a built in tests runner.

A small yellow circle will appear to the left of each class that is decorated with the [TestFixture] attribute, by clicking it, we can execute the entire fixture. In order to execute a specific test, we can click on the yellow circle to the left of that specific test. Resharper will show us a detailed report in a dedicated window (Resharper -> Windows -> Unit Test Sessions).

Epilogue

I hope that I managed to explain the importance and advantages of unit testing. The subjects on which I wrote about, are just the tip of the iceberg. For more information about the subject, I recommend the following books:


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images