Writing your first tests
Writing tests for your code isn’t difficult. It’s simply a matter of
fixtures, tests and asserts.
- An assert is a simple check. For example, that an integer is less than
another, that a variable is of a given type, or that a value in a database is the
expected one. The idea of asserts in .NET is not a new one, but in unit testing,
an assert is the most basic ingredient of the development process. MbUnit pre-defines
a lot of different asserts so you don't have to. It also leaves you free to build
your own as you need to.
- A test is a collection of asserts intended to prove that an action or series of
actions in your production code actually does what you think it does. For example,
that your SaveToDB() method has actually saved information to your database correctly,
that your Add() method really does add those values together or that the default
constructor for your class doesn't create a null object.
- A fixture is a collection of tests usually related to a single class in your production
code. It doesn’t have to be, but that’s usually how it turns out.
With that knowledge in your head, let’s write a few tests for an application
to help you win at the classic fizzbuzz game. The rules are easy.
- If a number is wholly divisible by 3, return the word fizz.
- If a number is wholly divisible by 5, return the word buzz.
- If a number is wholly divisible by 3 and 5, return the word fizzbuzz.
- If a number isn't wholly divisible by either or 5, return the number.
To keep things simple, we’ll just write the first few tests for a simple static
method called ToFizzBuzz() that takes an integer as an argument and returns the
correct fizzbuzz string.
Setting Up The Solution
There are two options when it comes to organising your test code in your solution
- You can write your tests in the same project as the code you are testing, as demonstrated over on our main
site.
- You can keep your tests in a separate assembly from the code you are testing, which
we'll demonstrate here. It takes a bit longer to set up but does mean your test
code won't be included in any live code you release.
First you’ll need to create two projects, one for the production code containing
ToFizzBuzz() and one for the test code.
- Open up Visual Studio and create a new c# class library solution. We've called
it FizzBuzz and renamed class1.cs to fizzbuzz.cs .
- Once it has been created, add another c# class library project to the solution
called FizzbuzzTests and renamed class1.cs to fizzbuzztests.cs.
- Add a reference to MbUnit.Framework.dll to the FizzBuzzTests project. You'll find
it in the .NET tab.
- You'll also need to add a reference to the FizzBuzz project to FizzBuzzTests.
- Solution explorer should now look something like this
That's you all set up. Now all you need to do are write tests and some code.
Writing the Tests
With the project setup, writing the tests is straightforward.
- First, add a couple of using statements to the top of the test class; one for MbUnit.Framework
so our tests will compile and one for the FizzBuzz class we're testing
- Next, you need to define a test fixture. Recall from above that a fixture is a
collection of tests defined in a class. To do that, we decorate the FizzBuzzTests
class with the [TestFixture] attribute. Easy. Your code should now look like this.
1: using System;
2: using FizzBuzz;
3: using MbUnit.Framework;
4:
5: namespace FizzBuzzTests
6: {
7: [TestFixture]
8: public class FizzBuzzTests
9: {
10: }
11: }
- Tests should always be written before writing code. So we start by making sure
that ToFizzBuzz() will return “1” when given the number 1 with the following
test. This exerc
10: [Test]
11: public void ToFizzBuzz_Send1_Returns1()
12: {
13: Assert.AreEqual("1", FizzBuzz.FizzBuzz.ToFizzBuzz(1));
14: }
As you can see, we have one assert in this test: that ToFizzBuzz() returns "1" when
we send it the integer 1.
- Now we run the test to make sure that it fails. We can do this with either the
console test runner,
the GUI runner, or another third party test runner such as
TestDriven.NET or Resharper.
The choice is yours. Follow the links to see how to run the tests.
- Now to write the simplest code that satisfies the test. Thus we have.
1: using System;
2:
3: namespace FizzBuzz
4: {
5: public class FizzBuzz
6: {
7: public static string ToFizzBuzz(int number)
8: {
9: return "1";
10: }
11: }
12: }
- And so the cycle continues. Our next test should be for the number 2. We could
write another test called ToFizzBuzz_Send2_Returns2() which basically copies everything
in the previous test except for replacing 1s with 2s. However, the key to refactor
everywhere if possible applies to our test code, and we can make use of MbUnit's
excellent RowTest facility and generalise the test we have for all numbers which
aren't divisible by 3 or 5.
10: [Row(1)]
11: [Row(2)]
12: [RowTest]
13: public void ToFizzBuzz_SendNumberNotDivisibleBy3Or5_ReturnsNumberAsString(int NumberToTest)
14: {
15: Assert.AreEqual(NumberToTest.ToString(),
16: FizzBuzz.FizzBuzz.ToFizzBuzz(NumberToTest));
17: }
It’s not the catchiest name, but it explains exactly what the test is testing
(ToFizzBuzz), how it is being tested (sending it a number which isn't divisible
by three or five), and the result it expects (returning the number as a string).
The [RowTest] attribute which has replaced [Test] tells MbUnit that this test must
be performed using the different values in each row given above [RowTest]. Those
values are plugged into the test as parameters - int NumberToTest, in this case
- so MbUnit now runs our test twice, once to test ToFizzBuzz() with the number 1
and again with the number 2. If you run the test again, you'll see it succeed once
(with the number 1) and fail once (against the number 2).
- To make both tests succeed, we need to make just a slight alteration to ToFizzBuzz()
7: public static string ToFizzBuzz(int number)
8: {
9: return number.ToString();
10: }
- And so we continue the cycle of writing tests and then writing the code that satisfies
them. There are three more tests to write for ToFizzBuzz.
- Sending it a multiple of 3 but not of 15 and it returning "fizz"
- Sending it a multiple of 5 but not of 15 and it returning "buzz"
- Sending it a multiple of 15 and it returning "fizzbuzz"
The first might look something like the following.
19: [Row(3)]
20: [Row(6)]
21: [RowTest]
22: public void ToFizzBuzz_SendNumberDivisibleBy3ButNot5_ReturnsFizz(int NumberToTest)
23: {
24: Assert.AreEqual("fizz", FizzBuzz.FizzBuzz.ToFizzBuzz(NumberToTest));
25: }
And so on. The point here is not how to write the best implementation of ToFizzBuzz()
- there are several equally good ones - but that writing tests is not any different
from writing another piece of code. The key is that your tests must be correct before
your code can be. If we added [Row(15)] to this latest test, it would expect ToFizzBuzz()
to return "fizz" rather than "fizzbuzz" as it should.
What MbUnit provides you with are methods such as the row test to let you perform
these tests with the minimum of coding required. To that extent, the MbUnit.Framework
library offers a great number of asserts for checking values, arrays, collections,
data, files and more for you to use and a number of different test types in addition
to the vanilla [Test] and strawberry flavoured [RowTest] you've seen here. It also
lets you specify a test’s author or category for review later on. Perhaps
you'll want to stop a few tests running for a while or flag issues with a test during
a run? You can do that too with ignore flags and warnings.
Everything mentioned here and a lot more is covered in our API
reference in detail. If you're not sure what to start, perhaps you should
check out this rough guide.
More Step by Step guides
Check out our articles page for
more step by step guides to writing code using tests.