12

My understanding of the TDD methodology is that (failing) test cases are written promptly after finalizing the requirements.

My understanding of the open-closed principle (in the context of OOP) is to structure the class hierarchy so that any new features involve writing new code instead of modifying old code.

Those two strike me as contradicting. Shouldn't instead the process be more like

  1. clear up requirements
  2. take a long walk away from the computer
  3. design a public api with minimal implementation (e.g. throw NotImplemented)
  4. implement all requirements as tests, make sure they all fail
  5. don't touch the dummy implementation. Inherit the interface by another class, which turns all tests green?
11
  • 4
    TDD is not writing all the test cases and getting it green one by one. TDD follows the cycle of red-green-refactor. Write one failing test(red) - Get the test to pass (green) - Refactor the code. <br> Open Close principle is about maintaining the structure of classes in a way that new features are added in new class without modifying the old. It would be practically impossible to be 100% open-close. (What about you startup/bootstrapping classes). The reason you have open close principle is you don't break old requirements when coding new. But if you test cases, they are reasonably taken care Commented Sep 14, 2020 at 2:15
  • @Vorac, I see no contradiction if tests are written after design stage, but before implementation. Could you clarify why that does not fit in your model?
    – Basilevs
    Commented Sep 14, 2020 at 2:22
  • 5
    TDD is a design philosophy. The Tests are the software specification. Tests written after the design process are not the same beast, even though they are written using the same techniques. Open/Closed is a property that is desirable to have, particularly in highly shared objects. Its not a design goal in its own right though.
    – Kain0_0
    Commented Sep 14, 2020 at 2:28
  • @Kain0_0 can you write tests before design? If they are written "during" design, then some relevant part of design is already done.
    – Basilevs
    Commented Sep 14, 2020 at 3:28
  • @Basilevs Yes there are obviously some design decisions that predate even writing a test. For example what language are you writing the test in. But Tests written as part of TDD are part of the design process, in that they are design specifications. Some of the specifications will have been pre-determined and simply encoded in the test. Others fall out as the specification is implemented, which would have been difficult to observe a priori, yet a posterori are blatantly obvious. These are observed during the refactor phase and extracted and codified as necessary requirements.
    – Kain0_0
    Commented Sep 14, 2020 at 3:44

2 Answers 2

5

It’s not contradictory, it’s complementary:

  • TDD is about writing tests to formalize and verify requirements. But it’s not about finalized user requirements: it’s about currently known design requirements. These translate/ transform some aspects of the user requirements in a way that makes sense in your design, and more specially take into account the distribution of responsibilities between your components.

  • OCP is about shaping your design in a way not to reinvent the wheel, but also not to break things that already work well. It allows you to specialize class, and benefit from the existing tests, and write new tests only for the specialized parts. (here I say specialization to mean extension).

So there is a synergy between the two that allows to quicker reach a stable and robust design:

  • OCP means not only clean design but also fewer tests to verify the same requirements
  • TDD will reveal weaknesses of the current design early, which could suggest need for refactoring
  • refactoring allows you to improve OCP if it wasn’t well thought initially.

This approach is therefore completely compatible with incremental or evolving user requirements, which will translate to new design requirements and refacotring.

For your API:

  • you can of course take a long walk and develop it mentally, challenging yourself, alone in your mind.
  • But you can as well develop it with TDD and OCP, using for example mocks and test doubles, to collaboratively, with your team, converge together to a very robust design.

It’s a matter of project size. The team simply has more brainpower than any individual that composes it and a team is not efficient in long abstract discussions in a long walk.

25

Frankly, I see at least three huge misconceptions in this question:

  1. what TDD is about, and

  2. what the OCP is about, and

  3. software is developed in a waterfall approach.

Let me start with the OCP. The OCP is a principle for producing reusable, generic, black box components or libraries. Such components may be developed and released by a vendor A, and then reused by a vendor B who has no direct control over the code (so it is closed against modification from B's point of view). But since A does not know the exact cases where B will reuse the component, they provide parametrization or extension points for the component - this is what open for extensions mean in the acronym OCP. Note that though the OCP is often explained using inheritance/polymorphism, this is not the essential characteristic of this principle.

In any business system of reasonable size, there will usually be a few components which follow the OCP, but most of them will not (except when you are in the role of the library vendor A and your task is to design nothing else but such components).

Now take the fact that requirements are not "finalized" (ar least, not all at once). Requirements are implemented one by one, each new one changes the existing system, the implementation can take influence on the design and change the basis for the next requirement to implement.

Whenever a new requirement is implemented in a system, there are parts of the existing code which have to be touched, extended and refactored. Components which fit to the OCP (at that time, and with regard to the specific requirement) can stay untouched, the code which uses these components will have to be adapted.

Now TDD comes into play. TDD is an implementation technique to write one test at a time, for the next "arriving" requirement (or "slice" of a requirement), before the requirement (slice) is actually implemented. Afterwards code gets written to make the test succeed and refactoring takes places. The refactoring may just clean up the code a little bit, but sometimes it can also be used to extract parts of the non-OCP compliant code and make it "OCP compliant", by introducing more parameters and extension points, or by extracting new reusable parts and components. So when the next requirement "arrives", one may be lucky and can reuse these parts of the existing code without any change.

I hope this made clear that TDD, refactoring, and the OCP are in no way contradictory: quite the opposite, TDD can actually help to develop OCP compliant components , and the OCP helps to build components which require less refactoring, less code changes and fewer tests.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.