Home > Unit Testing, Visual Studio > First Foray into Unit Testing with Visual Studio 2008

First Foray into Unit Testing with Visual Studio 2008

June 12, 2008

I’ve read about Unit Testing and Test Driven Development (TDD) but have never attempted to use it or even really understand it. I recently became intrigued by it watching Rob Conery’s excellent video series. Today I began putting the final touches on the Authorize.Net code I am going to release, so I thought this would be a good opportunity to try some testing.

I have never done any Unit Testing, and even though I wasn’t sure where to start, I figured it out pretty quickly. First things first, I added a Test Project to my solution. This created all the necessary framework code as one might expect. I deleted the default TestMethod and began adding TestMethods of my own, intuitively marked with the [TestMethod] attribute. A little Google hopping and I quickly learned about Assert.xxx() methods.

When it came time to actually run the tests, I was a little stumped. I read where you could right click the method name and execute “Run Tests”, but it seems to only run one at a time. In the test results window, there is a button for “Run All test”, but it too only ran the last executed test. Finally, I changed the Test project to my Solution Start Project, and pressing F5 to execute will run all the tests.

Here is the code from my first stab at it:

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DevelopingForDotNet.AuthorizeNet;

namespace TestAuthorizeNet
{
    ///
    /// Summary description for UnitTest1
    ///
    [TestClass]
    public class UnitTest1
    {
        public UnitTest1()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        ///
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod]
        public void TestCreditCardNumberWithCharactersConvertedToJustNumbers()
        {
            TransactionRequestInfo req = new TransactionRequestInfo();
            string testValue = "1234-5678-9012-3456";
            req.CardNumber = testValue;
            Assert.AreEqual("1234567890123456", req.CardNumber);
        }

        [TestMethod]
        public void TestZipCodeNotNumericThrowsArgumentException()
        {
            TransactionRequestInfo req = new TransactionRequestInfo();
            string testValue = "034JB";
            try
            {
                req.Zip = testValue;
                Assert.Fail("Exception not thrown.");
            }
            catch (ArgumentException aex)
            {
                Assert.IsTrue(true, "Exception Thrown Properly");
            }
        }

        [TestMethod]
        public void TestZipCodeNumericButGreaterThan99999()
        {
            TransactionRequestInfo req = new TransactionRequestInfo();
            string testValue = "58634789";
            try
            {
                req.Zip = testValue;
                Assert.Fail("Exception not thrown.");
            }
            catch (ArgumentException aex)
            {
                Assert.IsTrue(true, "Exception Thrown Properly");
            }
        }

        [TestMethod]
        public void TestZipCodeValidReturnsLeadingZeros()
        {
            TransactionRequestInfo req = new TransactionRequestInfo();
            string testValue = "586";
            req.Zip = testValue;
            Assert.AreEqual("00586", req.Zip);
        }
    }
}

A few notes worth mentioning:

  1. I found a bunch of sites that talked about Testing in Visual Studio, about half and half claiming it was a good thing. What I did not find so readily was a good tutorial. I did finally come across a good post at http://www.geekzone.co.nz/vs2008/4819, a blog I had never visited before.
  2. What I have done so far would certainly not qualify as TDD, I’m just experimenting at this phase. I did, however, try to write tests that failed before correcting them to pass, but they were all on pre-existing methods.
  3. I tried to follow the “use long, descriptive test method names” rule.
  4. I need to understand mocking. Right now, I am creating full fledged class instances.
  5. It seems a tad slow. I can’t imagine what this would be like with hundreds of tests to run. I’m sure there are ways to handle this issue.

My plan is to include the test Project along with the entire Authorize.Net Solution. Whether it will be helpful or not, I don’t really know.

Advertisements
  1. Dan Blair
    June 12, 2008 at 3:40 pm

    MSDN had a nice article on using mocks.
    http://msdn.microsoft.com/en-us/magazine/cc163904.aspx

    As for unit tests, they are just indispensible once you start using them. As to the religious wars between Team test and NUnit, etc. The important side is that you use the tests, you can run them at any time, and you have some ability to report on them. The tool doesn’t matter that much.

  2. June 13, 2008 at 7:53 am

    Thanks, I’ll be sure to read it.

    Even with my extremely limited experimentation I can see that they are going to become a staple of my development diet. I know there are people who are diehard NUnit fans, but I’m sure it really doesn’t matter what flavor you prefer. For me, I’ll probably stick with the built in Testing mechanism, just because it is easy and readily available.

  3. Thomas Eyde
    June 14, 2008 at 12:00 pm

    As you have discovered, adding unit tests after the fact is very hard work. I can tell you, once you get the habit of writing them first, one by one, you will find them easy to write and quite indispensable.

    A tip for the tests you have shown us here:

    The assertion message is only displayed when something is broken, hence you want the message to explain what’s wrong. The way you have written these tests, the second assertion will always be true and the message never displayed.

    If you are comfortable with empty catches, you can just delete the second assertion.

    If the exception type is important, you could write the test as follows:

    [TestMethod]
    public void TestZipCodeNumericButGreaterThan99999()
    {
    TransactionRequestInfo req = new TransactionRequestInfo();
    string testValue = “58634789”;

    ArgumentException thrownException = null;
    try
    {
    req.Zip = testValue;
    }
    catch (ArgumentException aex)
    {
    thrownException = aex;
    }

    Assert.IsNotNull(thrownException, “Proper exception not thrown.”);
    }

  4. June 16, 2008 at 7:02 am

    Oh, I like that! Yes, I have plenty to learn. I don’t think adding them after the fact is difficult, per se, but I can see where you would easily write tests to support or validate what you’ve already done. In my mind, that defeats the purpose.

    Also, one of the articles I read mentioned that by using TDD, you only develop the code you need. I have a tendency to write a bunch of overloads and methods “just in case”, so this will be a great benefit for me. I think ultimately it will make me a faster coder, and I’m sure a more productive one.

  5. Dan Blair
    June 16, 2008 at 10:44 am

    The just in case methods are the first things that I saw go when we started focusing on using unit tests. If you write what you need, you know when your done, have a reasonable expectation of it being correct, and the code should be more maintainable.

    I come across code all the time that’s 8 year old “just in case” code that has never been used. Some of it took several weeks/months to develop, we really do nobody a service by creating such things.

  6. Thomas Eyde
    June 19, 2008 at 7:50 am

    I read the other day, I don’t remember exactly where, but it said that writing tests first make us focusing better on the task at hand, and that activity alone makes the quality go up by an order of magnitude.

  7. June 19, 2008 at 8:08 am

    I think that’s a big part of it Thomas. I frequently find myself starting to code with a rough idea of what I want, but without much of a game plan. When I realize I’m doing this, I try to stop and at least outline what it is I want to accomplish. I think by writing the tests first, you start with two important pieces of information: the definition of success AND the definition of failure. Since everything other than success constitutes failure, and we start without success, it makes sense to start with a failing test and code *just until* we achieve success.

  8. August 12, 2008 at 8:25 am

    One thing I’d like to respond to in the comments here… Adding unit tests that validate what you’ve already done doesn’t defeat the purpose at all. You might not be getting the full benefit, but it’s still valuable. If you ever have to come back to that code and make changes, having the tests gives you a much better chance of picking up any bugs you might happen to introduce.

    If anything, my experience has been that in the long term, unit tests are really handy up-front but the real value is in the confidence they give you when you inevitably have to modify the code later… especially after your old, half-forgotten tests have caught a few bugs for you…

  9. August 12, 2008 at 8:49 am

    That’s a great point Chris. I’m sure we all know that once we write it, it is ours forever (or at least as long as we work in the same place).

    To be clear, though, this is what I said:

    …you would easily write tests to support or validate what you’ve already done. In my mind, that defeats the purpose.

    What I mean by this is that when writing tests after the fact, we have to be careful to make sure they are testing the intent of the code and not just verifying the result we are already getting, since the two may not match. I do agree that adding the tests has value, we just can’t cook the testing books so to speak.

  1. June 26, 2008 at 9:41 am
  2. December 1, 2008 at 4:48 am
  3. November 30, 2010 at 2:10 am
Comments are closed.
%d bloggers like this: