Writing good unit test cases is hard and requires you to be paying a lot of attention to catch problems.

Consider for example, this operator/ (vector division) method for a Vector class (NOT STRUCT!)

( Since class Vector is a class and not a struct, vec will be passed by reference. )


  public static Vector operator /( Vector vec, double divider )
  {
    vec.x /= divider;
    vec.y /= divider;

    return vec ;
  }

Above code to be used as in:

Vector v = new Vector( 15, 5 ) ;
Vector w = v / 5 ;

Can you see the bug?

You really have to be paying very close attention to ideas that the original programmer (frequently you) might have overlooked when developing the method.

Remember, Vector is a class, not a struct!

Here’s a naive first attempt at Unit test for the operator/ method:

    [TestMethod()]
    public void op_DivisionTest()
    {
      // make a vector and divide it by a constant
      Vector vec = new Vector( 4.92, 8 ) ;
      double d = 7.7 ;
      Vector actual = vec / d ;

      // Find what it should actually be:
      Vector expected = new Vector( 4.92/d, 8/d ) ;

      // Assert result is what you expect
      Assert.AreEqual( expected, actual );

    }

Can you see the problem? Can you see how this unit test FAILS to catch the real problem with this method?

Look again at the original operator/ method at the top of this page. The problem with the method is it mutates vec, since vec is passed by REFERENCE only and not by value (the method would be FINE if class Vector were struct Vector instead!

So, here’s the better unit test that catches this problem:

    [TestMethod()]
    public void op_DivisionTest()
    {
      // Compute a result, and find expected value as well
      Vector vec = new Vector( 4.92, 8 ) ;
      double d = 7.7 ;
      Vector actual = vec / d ;

      Vector expected = new Vector( 4.92/d, 8/d ) ;

      // Test result is what you expect:
      Assert.AreEqual( expected, actual );

      // This is "writing good unit tests".  TEST FURTHER.
      // What else should be guaranteed by this method / what
      // is the programmer to naturally assume about how the
      // method behaves and what it does to its inputs?

      // 1.  A new object, distinct instance of an object should be produced.
      Assert.AreNotSame( vec, actual ) ;

      // 2.  vec should remain unchanged from its original value
      Assert.AreEqual( vec, new Vector( 4.92, 8 ) ) ;

    }

The corrected operator/ method

For instance, here is an operator/ division method for a Vector class (NOT STRUCT!)
  public static Vector operator /( Vector vec, double divider )
  {
    Vector result = new Vector() ;

    result.x = vec.x / divider;
    result.y = vec.y / divider;

    return result ;
  }

So its easy to write crummy simple unit tests that don’t catch everything!! Unit tests aren’t catch-all.. you have to write them cleverly!

Post a Comment