UnitTesting

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, horizontal is cost)
    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 where to replace the "star" in the middle would take a lot of work:


      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 use 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 Kata 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);
            }
        }
    
    }
    
  7. A Few Other Types of Testing:
    1. Integration Tests

      Do unit tested component work well together?

    2. System Tests

      Do all the real components work well together, (no test double objects used)?

    3. Load Test

      Does the system perform at or above expected levels of use?

    4. Endurance Test

      Can the system perform at high levels of use for extended periods of time?