Home > BDD, XP > Don’t test your privates!

Don’t test your privates!

This blog post brought to you by the wonderful ambiguity of the English language!

And now back your regularly scheduled program ….

“How do I/Should I test private methods?”

This question seems to come up quite often. In this article I hope to convince that not only do you not need to test your private methods but that it is also bad practice.

Do what do I mean when I say “don’t test your private methods”? Am I telling you not to test code that might break? Not at all. What I mean is when you follow these two principles:

  1. Code from the outside-in
  2. Code to an interface, not an implementation

you will not need to go to any lengths to test your private methods. The first principle is why you won’t need to test private methods and the second is why it’s bad practice to test them. To better understand this lets look an example:

We’ll start with a test


procedure MyStack.ShouldPushItems;
begin
  FStack.Push('foo');
  CheckEquals(1, FStack.GetCount);
end;

And now lets fill in some code to make it compile:


type
  TMyStack = class(TObject)
  private
    FData : TStringList;
    procedure DoPush(const Value : string);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Push(const Value : string);
    function GetCount : Integer;
  end;

implementation

procedure TMyStack.DoPush(const Value : string);
begin
  FData.Insert(0, Value);
end;

procedure TMyStack.Push(const Value : string);
begin
  DoPush(Value);
end;

function TMyStack.GetCount : Integer;
begin
  Result := FData.Count;
end;

No surprises here (except that I left out the constructor and destructor code). The test now passes with this code. Now how do we make sure that the value we pushed onto the stack was actually stored? That is to say, how do we know DoPush() did its job? How do we test DoPush()? We can’t look at the internal storage because it’s private and we don’t want to break encapsulation for testing. We don’t yet have a Pop() method as our test didn’t call for it.

So what do we do? Right now our testing context is very generalized and non-specific; just a stack. Let see what changing our testing context could get us:


procedure MyStackWithOneItem.SetUp;
begin
  FStack := TMyStack.Create;
  FStack.Push('foo');
end;

procedure MyStackWithOneItem.ShouldHaveACountOfOne;
begin
  CheckEquals(1, FStack.GetCount);
end;

procedure MyStackWithOneItem.ShouldPopThePushedItem;
begin
  CheckEquals('foo', FStack.Pop);
end;

And now some code to make it compile:


{ this is a private method }
function TMyStack.DoPop : string;
begin
  Result := FData[0];
  FData.Delete(0);
end;

function TMyStack.Pop : string;
begin
  Result := DoPop;
end;

So now our testing context is a stack with one item pushed onto it. We already know that ShouldHaveACountOfOne() will pass since we proved it in the previous iteration of the code. However, we still need to prove that the item we pushed onto the stack was indeed stored. That’s where ShouldPopThePushedItem() comes in. We’re able to infer that DoPush() did its job by verifying it using Pop().

But wait! What about DoPop()! We didn’t test that! You right … while we did check that the value returned from Pop() matched our pushed item there is one behavior of DoPop() that we didn’t check: the stack should now be empty. Let add a test:


procedure MyStackWithOneItem.ShouldBeEmptyAfterPop;
begin
  FStack.Pop;
  CheckEquals(0, FStack.GetCount);
end;

Remember that DUnit runs SetUp() before each test. ShouldBeEmptyAfterPop() will find the stack containing one item even though it executes after ShouldPopThePushedItem().

By changing our testing context we were able to verify that our private methods worked the way we expected them to.

Now, why is it bad practice to test private methods? Well lets say we made some decisions that led us to expose the internal data storage so that we could verify that Push() worked by accessing the internal storage. What happens when we refactor the code to use some other form of storage? This is where “Code to an interface, not an implementation” comes in. In our case Push(), Pop() and GetCount() make up the interface to the stacks implementation. Our test shouldn’t care about how the stack does its work any more than our customers care. As long as Push(), Pop() and GetCount() work the way they’re expected to work then all is well.

Given this arrangement let say that we decide to refactor the stack to store its data in database. I’ll leave the code for that as an exercise for the reader (!). Even though this is a large change to the code we can proceed with confidence. Why? Because our tests will tell us when we’ve broken something. Why can we rely on the tests? Because when we embarked on this big change the tests were passing and as we make this change, we don’t have to change the tests.

So, please, don’t test your privates. =)

BDD, XP

  1. John E
    May 18th, 2007 at 14:34 | #1

    Great thoughts.

    Another way to look at this is if you have a private method that does not effect the public method behaviors, then delete the private method code… it does nothing (or it maybe some type of optimization, then do not delete it). If you test first or design to interfaces, etc… you will not be writing code that you can not test.

    If you feel you need to test the privates, then this is a code smell and it is time to extract that behavior into it’s own class.

  2. ABV
    January 8th, 2009 at 11:37 | #2

    Let’s consider Facade pattern. My class users need the 2 public methods. They would be too large, so I’ve made 10 private methods (re-used), and 2 public methods calling them. Actually, my private methods should not necessarily be private; they’ll not break the internal state of instance. But:
    1) Publishing them means complexity for users (they have a choice that they don’t need)
    2) I cannot imagine TDD style for such a large methods, when to return something you’re to write 100+ lines of code.
    3) Really, data for this method is retrieved from database, and testing DB-related functionality is much more difficult.

    When I’m testing private methods:
    1) I don’t publish details that would confuse users.
    2) I can work in TDD style (write small methods step-by-step).
    3) I can cover most of class’s functionality using test data, without database connection.

  3. January 9th, 2009 at 14:12 | #3

    It sounds like your Facade is actually doing work. Facade’s purpose is to provide a simplified interface to a larger body of code. As such the code in Facade should really do very little.

    BTW, is this your thread on StackOverflow? http://stackoverflow.com/questions/425709/unit-testing-private-methods-facade-pattern

    Facade should be very lightweight. Be wary of imposing too much responsibility, work or data on it.

  4. March 26th, 2010 at 02:12 | #4

    Thanks for sharing. Keep up the good work.

  5. May 27th, 2010 at 02:40 | #5

    No man in the whole world is able to compete with famous essay papers service in sample term papers creating. Because the custom academic writing services employ professional essay writers.

  6. August 3rd, 2010 at 18:15 | #6

    I appreciate yout report.

  7. August 21st, 2010 at 05:21 | #7

    I found your website perfect for my needs. It contains wonderful and helpful posts. I have read most of them and got a lot from them.

  8. August 23rd, 2010 at 05:43 | #8

    It help me very much to solve some problems. Its opportunity are so fantastic and working style so speedy.
    I think it may be help all of you. Thanks a lot for enjoying this beauty blog with me.

  1. No trackbacks yet.

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word