views:

132

answers:

4

I'm learning TDD, and I have a question about private / protected variables. My question is: If a function I want to test is operating on a private variable, how should I test it?

Here is the example I'm working with:

I have a class called Table that contains an instance variable called internalRepresentation that is a 2D array. I want to create a function called multiplyValuesByN that multiplies all the values in the 2D array by the argument n.

So I write the test for it (in Python):

def test_multiplyValuesByN (self):  
    t = Table(3, 3) # 3x3 table, filled with 0's
    t.set(0, 0, 4) # Set value at position (0,0) to 4
    t.multiplyValuesByN(3)

    assertEqual(t.internalRepresentation, [[12, 0, 0], [0, 0, 0], [0, 0, 0]])

Now, if I make internalRepresentation private or protected, this test will not work. How am I supposed to write the test so it doesn't depend on internalRepresentation but still tests that it looks correct after calling multiplyValuesByN?

+10  A: 

You should not depend on the internal representation of an object. That's why it is marked as private or protected. Consider what observable changes are made to t when you call t.multiplyValuesByN(3). Then, test on what you can observe.

def test_multiplyValuesByN (self):  
    t = Table(3, 3) # 3x3 table, filled with 0's
    t.set(0, 0, 4) # Set value at position (0,0) to 4
    t.multiplyValuesByN(3)

    assertEqual(t.get(0,0), 12)
Bryan
A: 

Don't test for private variables/state. Your tests should confirm that the unit under test conforms to its specification, and that specification is determined by its interface. So your test should be written in terms of the inputs to your test unit, and verify that the output matches what you expect.

You want to be able to change the implementation of the unit under test (for, say, efficiency reasons) and yet confirm that it works as expected. So checking private state would cause difficulties in this situation.

Brian Agnew
+3  A: 

If it is internal, then it is nobodys business outside the class and that includes the tests.

In TDD you are designing the API of your classes and the future clients also can only see the observable behavior of the class.

I know this is easier said than done.

In test you often see a pattern reoccuring : setup - operate - check (- teardown)

The setup phase is responsible to bring the object in the preconditions state.

The check phase should verify the postconditions through observable behavior of the class. It makes no sense to store invisible state, if it never and nowhere comes out.

Peter Tillemans
...also known as Arrange-Act-Assert (but with no alliterative match for Teardown - guess it's a good thing it's not much used!)
Carl Manaster
Yep, that's why I put it in parentheses. It's one of those practical things which are sometimes needed but do not fit well in the theory. I like the 3-A alliteration. Have not heard that before, Thanks!
Peter Tillemans
+3  A: 

Others have posted good answers, but IMHO failed to emphasize one thing: the design aspect (although Peter Tillemans mentioned it). So I add a bit of an explanation on this.

When doing TDD, you are effectively testing the design of your API as well as the implementation. If you find that the result of a method call is difficult or impossible to see from outside, this is almost always a sign that your class interface is not designed well. If it is difficult to write tests for your class, it will usually be difficult to use it in real life too - your unit tests are in effect the first clients of your class. So if you see that a test case has difficulty using your class interface, you should consider going back and redesigning your API to make it easier to use (not compromising encapsulation, if possible).

Péter Török