« | »

Test the interface, not the implementation

5th February 2010

In yesterday’s post, I mentioned that unit tests should test the interface and not the implementation. This is the simplest example I could think of to demonstrate this. Consider this class.

package somePackage {
  class SomeClass {
    private var _x:int;

    public function getX():int {
      return _x;
    }

    public function setX( value:int ):void {
      _x = value;
    }
  }
}

[N.B. I'm using standard methods rather than AS3 getter/setter methods because it makes the example clearer.]

Now, how do you test it? One temptation is to write a test for each method (many books on TDD suggest treating each method as a unit for testing is a good place to start). To do this, the tests would have to look something like this (in FlexUnit 4).

package testsPackage {
  import somePackage.SomeClass;

  class SomeClassTest {
    [Test]
    public function testGetX():void {
      var obj:SomeClass = new SomeClass();
      obj._x = 10;
      Assert.assertEquals( obj.getX(), 10 );
    }

    [Test]
    public function testSetX():void {
      var obj:SomeClass = new SomeClass();
      obj.setX( 10 );
      Assert.assertEquals( obj._x, 10 );
    }
  }
}

Obviously, this doesn’t work because _x is private. But there are ways around that.

  1. We could make _x protected and make our test class extend the class we’re testing.
  2. We could make _x internal and place the test in the same package as the class
  3. We could define a new namespace and define _x in that namespace

If we did this, we’d have good test coverage. However, we’ve compromised the class to make it testable. We’ve also made a test that is so tightly coupled to the implementation that there is no room for refactoring.

None of these is a good idea. We shouldn’t need to access _x just to test the class. The private _x variable is part of the implementation, and our test shouldn’t depend on the implementation.

If we consider the purpose of the class and the methods and properties used to interact with it (the interface), we realise that all we should care about is if we call setX with a value, then when we call getX we should get that same value back. So the test should look like this.

package testsPackage {
  import somePackage.SomeClass;

  class SomeClassTest {
    [Test]
    public function testX():void {
      var obj:SomeClass = new SomeClass();
      obj.setX( 10 );
      Assert.assertEquals( obj.getX(), 10);
    }
  }
}

If you find yourself wanting to access private methods of a class for your tests, it’s probably because you’re testing the implementation rather than the interface.

N.B. I’d actually test x using a range of values (not just 10) covering edge cases and likely failures. Likely values include negative numbers, maximum/minimum values and zero.

Tags:  

3 Comments add your own

Leave a Comment comment policy

required

required, hidden

XHTML: you can use these tags - <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe to the comments via RSS Feed