Saturday, May 30, 2009

Unit Test Presentation

[This is the outline of a presentation I gave recently at our company on unit testing. Permanent link is here.]

What is Unit Testing?


Take the smallest reasonable piece of code, isolate it from the rest of the application, and programatically query it to ensure the results are what is expected.


  1. Bad Things about Unit Testing:
    1. Unit Tests are expensive to write and very expensive to maintain.
    2. You can test too much.


      Bell Curve


      (Vertical axis is return on investment)
    3. You can test the wrong things.
    4. You can easily develop a false sense of security when all your unit tests pass.
    5. Having 100% code coverage doesn't mean your application is error-free.


      graphics.
    6. When all your unit tests pass, it doesn't mean your application is error-free.


      graphics.
    7. When all your unit tests pass, it doesn't mean your application is error-free. [sic]

  2. Why Do Unit Testing?

    1. Encourages better software design - less coupling, smaller, simpler methods

      Instead of tightly interlocked code like this:




      Tangled Web


      Unit testing encourages code more like this, easy little blocks that can be replaced easily:


      Tangled Web

    2. Allows you to make deep changes in the software later with confidence
    3. Produces better quality software
    4. Gives you a framework for performance testing
    5. Finds errors in single components early, instead of finding multiple errors in multiple components which is exponentially more difficult.
    6. Easier to catch threading issues in unit tests
    7. The tests themselves are documentation
    8. It's more fun - really.


  3. The Process of Unit Testing

    1. Write the test before creating any logic in your target method
    2. The first run of the test should prove it fails

      Red Test
    3. Write the simplest code in the target method to pass the test

      Green Test
    4. Refactor as needed.
    5. Repeat
    6. Red, Green, Refactor


  4. Notes Unit Testing

    1. Don't confuse Unit Tests with integration tests or system tests.
    2. Unit tests should be independent of each other.
    3. Unit tests should not typically hit external entities like a database. The test should use a mock instead. This requires external object access to be done through an interface, not a concrete class. This improves your design.
    4. Unit tests do not obviate the need for human testing of the system
    5. Unit tests allow you to throw exceptions easily in code. It's hard to simulate some network faults, but with a mock object it's easy.
    6. Many Unit test frameworks are available. We use NUnit.
    7. Unit Testing is critical to Agile software development.
    8. Where to put unit tests? In the object itself? In same assemble? In other assemble?
    9. Unit tests should be fast, less than a minute. To make them faster, move integration tests to separate suite, don't talk to the database, skip some on your local test box my using categories, and only run those on the build server, e.g., CruiseControl.Net.
    10. Each unit test should create it's own data, and delete it when it's finished. Integration tests should start with a clean database, add needed schema and data, then end with a clean database.
    11. When you find a bug, write a test that exposes that bug, and make sure it fails. Fix the bug, then run the test.


  5. Interesting Attributes in NUnit:

    1. [TestFixture]
    2. [Test]
    3. [ExpectedException]
    4. [Ignore]
    5. [Explicit]


  6. Our Dojo exercise:

    Build a case-insensitive ordered string set class. We will implement, Add(string), Count(), Contains(string), Remove(string), and GetEnumerator()



    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using NUnit.Framework;

    namespace Utilities {
    // Case Insensitive Ordered String Set class
    public class CiosSet {
    public CiosSet()
    {
    }

    public void Add(string mystring)
    {

    }

    public int Count()
    {
    return 0;
    }
    }
    [TestFixture]
    public class CiosSetTest
    {
    [Test]
    public void Should_add_a_string_and_get_count_of_one() {
    Console.WriteLine(MethodBase.GetCurrentMethod());
    //Arrange
    var set = new CiosSet();
    //Act
    set.Add("abc");
    //Assert
    Assert.IsTrue(set.Count() == 1);
    }
    }

    }

No comments: