4

When writing object-oriented code from scratch, I try to follow these steps:

  1. Write a test that fails.
  2. Write a function that makes the test pass.
  3. When enough tests and functions are complete, refactor functions with similar signature into a class.
  4. When the class functionality is complete, make private as many of the class methods as possible.

The last step seems sound to me, since object orientation is all about hiding complexity behind abstractions, and keeping the public part of those abstractions as small as possible.

However, this leads me to discard many of the tests that I wrote when still working with plain functions — since they are now testing the private part of a class' API. This makes me uncomfortable because:

  • remaining tests are suddenly forced to "cover more complexity".

    For instance: if I wrote a method to swap_rows in a matrix while inverting it, but that method is now private and only used from the public method invert, then the test_invert has to somehow convey to the reader that "swapping rows happens while inverting".

    More importantly, that remaining test possibly will not pinpoint that it is the swapping of rows that has been broken by a careless change (and not any other part of the inverting process).

  • private class methods may become public again when writing client code that has a reasonable cause to access their results.

    This is difficult to foresee. Recovering the tests from a previous commit is possible but it feels like wasted time.

A solution would be to refactor the methods out into a helper class that is package private. However, this looks like abusing object-oriented principles for the sake of TDD principles; also, it makes me wonder if there will ever be a good reason to make something private at all.

I realize I am asking here about "how to grow an architectural design", and that this cannot be done in a completely "bottom-up" approach; but I would like practical advice on keeping or discarding such early tests while still figuring out the shape of a piece of software.

This question is similar, but here I do not question that it is possible to get an initial public API right with TDD: I seek for good advice on how to get there. This answer is useful, but I do not think I am trying to adapt my coding to my tests, but rather my tests to my — still uncertain — design.

To summarize: should I keep or discard early tests while writing object-oriented code from scratch? Should I still strive to make as many methods as possible private, or is this leading me to a bad design and the forced removal of tests is just a symptom thereof?

logc
  • 2,190
  • 15
  • 19
  • 1
    TDD is not a silver bullet and is known for affecting design negatively, here is a recent set of TDD-mentor-hype-free discussions: http://martinfowler.com/articles/is-tdd-dead/ – Den Jul 16 '14 at 13:23
  • @KilianFoth - The most voted answer on the question you link to proposes the same solution I mentioned in my question: to make methods ("components") not private but package private. It makes me wonder when, if at all, one would think of tests as 'obsolete' and remove them, and let the corresponding implementations truly private. The accepted answer to the other question is that tests that cover code through several layers are 'integration tests' -- and I wonder what is the line between 'unit' and 'integration'. But both are valid points, and I have to think about them further; thanks! – logc Jul 16 '14 at 13:25
  • You are doing what is quite similar to my approach. I have a few differences though. [1] I prefer tests which test a logical flow of a feature which often results in the tests being integration tests than unit test. [2] I don't necessary write tests before code. The benefit of [1] is that I would not often run into problems like yours. That is tests being deprecated because module signatures change. I hoping to see more insight from seniors here to refine my approach. – InformedA Jul 16 '14 at 13:54

1 Answers1

3

IMMO the problem its that you are thinking in testing functions and classes instead of thinking in testing the exposed behavior of your system.

When your are testing exposed behaviors you only change your test when you change the behavior your system exposes; this includes removing tests when some behavior is not exposed anymore.

As a drawback to this, sometimes testing only exposed behavior is difficult because this behavior implementation is complex, and probably you want to do some internal testing too. This of course depends entirely in the complexity on the system but I recommend only make separated test for those things you think are more stable, for example some internal complex algorithms or things like that.

logc
  • 2,190
  • 15
  • 19
AlfredoCasado
  • 2,159
  • 11
  • 11
  • You may be right -- though I must defend myself by saying I am aware of BDD! :) Just to make things clear: do you think one has to alternate between thinking about behaviors and thinking about functions? I can imagine a class called `User` having a behavior, but what about a `Matrix` class? – logc Jul 16 '14 at 13:54
  • Matrix operations are behaviors of a mathematical library for example. IMMO try to think always in behaviors, perhaps you discover some subsystem within your system and some generic components you can extract. – AlfredoCasado Jul 16 '14 at 13:57