Tuesday, November 26, 2013

Unit Testing, Part 2 (A Tutorial on Creating Your First Unit Test in C#)

Intro

Back in Part 1 we talked about what unit tests are, why they are helpful, and when to use or not use them. This week we'll start coding our own unit tests. Never created a unit test before? Well now's the time! It really is quite easy, and the Visual Studio environment (even the latest express editions) makes it pretty painless. If you don't have a copy of Visual Studio already, you can click this to download Visual Studio Express 2013 for Web. For the examples below I am using Visual Studio Express 2013 for Web. If you are using a different version of VS your screens may look a little different.

Start the Project

The first step on the journey is to create our sample project. Fire up Visual Studio and create a class library (dll). Name the project/solution ItcProgBlogUnitTests.The IDE will create a default class for you named Class1. Open Class1.cs if the editor isn't already opened up for you. Create a single method in this class called AddStuff(). AddStuff should accept 2 parameters, both of type Nullable<int> (or int?). The return type should be int. This method should, assuming both integers have a value, return the sum of the 2 numbers. If one or both of the ints are null then return -1. If you want to take a sneak peek the method is pasted here, otherwise give yourselves a heapin helpin of pats on the back as you write it yourself.

        public int AddStuff(int? param1, int? param2)
        {
            if (!param1.HasValue || !param2.HasValue)
                return -1;
            else
                return param1.Value + param2.Value;
        }


Add a Unit Test Project

Tweak things until the project compiles; shouldn't take too long. Now add a unit test project to the solution. Name it ItcProgBlogUnitTests.Test.



Create a Unit Test

You should now have a brand-spankin-new unit test project in the solution. You should also have a single test class named UnitTest1 (yours may be slightly different) with a single TestMethod in the class.

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }

There's nothing much going on here...notice that attribute [TestClass] on the class? This tells visual studio and MSTest (the testing framework that we're using) that this class is used for running unit tests. Your test method also has an attribute on it [TestMethod], which denotes that this method is an individual unit test. We now want to write a unit test that ensures the method AddStuff returns the value -1 when we pass in a null for both parameters. The first thing we need to do is add a reference to ItcProgBlogUnitTests (the initial class library project) into our unit test dll, so do that. I'm assuming you already know how to add references to another dll within the solution, but if not put something in the comments below and we'll help you out. You'll also have to add a line to the usings statement within the file UnitTest1.cs so do that too.

using ItcProgBlogUnitTests;
Now go ahead and refactor that single test method to call it AddStuffNullParams. Then add the 2 new lines shown below to the method:


        [TestMethod]
        public void AddStuffNullParams()
        {
            var actual = new Class1().AddStuff(null, null);
            Assert.AreEqual(-1, actual, "AddStuff should return -1 when both parameters are null");
        }

Save your project and compile it; all should be well.

Run a Unit Test

You might be thinking to yourself "well great, my code compiles but now what Mr Smartie Pants?". Well first, that's Monsignor smarty pantaloons to you. Second, now we get to run the unit test! This is really quite easy; if you look in the screenshot below, you can see 2 methods of doing so. Go ahead and do one of them.



The "Test Explorer" window now appears within Visual Studio. This shows you the result of your unit test run so you can make sure the test passed.



As you can see, our lonely little test passed with flying colors. Green=good, Red=bad.

 

Watch a Unit Test Fail

Now let's pretend another coder comes along and decides that you were an idiot; he says to himself smugly "the method AddStuff should CLEARLY throw an exception if both parameters are nulls." That chode happily modifies AddStuff to look like the following:

        public int AddStuff(int? param1, int? param2)
        {
            if (!param1.HasValue && !param2.HasValue)
                throw new Exception("doh!");
            if (!param1.HasValue || !param2.HasValue)
                return -1;
            else
                return param1.Value + param2.Value;
        }


Chode-boy saves, compiles, and runs the unit tests. What's that he says? A failed unit test! Yes that's right folks, your diligently created unit test has saved the chode's bacon. He can tell quickly that the code no longer passes muster and thus must be fixed.



He can double-click on the failed test and be taken straight to the failed test in the code editor, he can right-click (see above screenshot) to choose among the many options for that test, or he can hang his head in shame. Maybe a combination of the above options.

Just below the list of tests is a detail section that describes why the test failed. In this case, it's because the method AddStuff threw an exception during our test run.



What's Next?

There are tons more options for unit tests; explore, play around, see what all you can do. The best thing you can do for now, assuming you've been following along, would be to first fix the broken method so that the unit test passes again, and then create some more unit tests to fully flex the method's muscles. You want to hit all the edges/conditions, and a few have been missed after all! If you want a quick spoiler, look down a few lines. I suggest you try it on your own for a little bit first before doing so though. Happy coding everyone!



The aforementioned spoiler...one potential list of possible unit tests:
  • both parameters null, return -1 (already done)
  • param1 is null and param2 is not null, return -1
  • param1 is not null, param2 is null, return -1
  • neither parameter is null, make sure the result is the sum of the numbers

No comments:

Post a Comment