Author: Haroon Khalil

  • Our first TDD session

    For this example, we will create a program that converts Roman numerals to integers. Roman numerals represent numbers with seven symbols: To represent all possible numbers, the Romans combined the symbols, following these two rules: For instance, the number XV represents 15 (10 + 5), and the number XXIV represents 24 (10 + 10 –…

  • Introduction

    Software developers are pretty used to the traditional development process. First, they implement. Then, and only then, they test. But why not do it the other way around? In other words, why not write a test first and then implement the production code? We discuss this well-known approach: test-driven development (TDD). In a nutshell, TDD challenges our traditional…

  • The Hexagonal Architecture and mocks as a design technique

    Now that you know about the Hexagonal Architecture and the idea of ports and adapters, we can talk about mocks as a design technique. In a nutshell, whenever mockists develop a feature (or a domain object) and notice that they need something from another place, they let a port emerge. As we saw, the port…

  • Static methods, singletons, and testability

    As we have seen, static methods adversely affect testability. Therefore, a good rule of thumb is to avoid creating static methods whenever possible. Exceptions to this rule are utility methods, which are often not mocked. If your system has to depend on a specific static method, perhaps because it comes with the framework your software…

  • Private methods and testability

    A common question among developers is whether to test private methods. In principle, testers should test private methods only through their public methods. However, testers often feel the urge to test a particular private method in isolation. A common reason for this feeling is the lack of cohesion or the complexity of the private method.…

  • Complex conditions and testability

    We have seen in previous chapters that very complex conditions (such as an if statement composed of multiple boolean operations) require considerable effort from testers. For example, we may devise too many tests after applying boundary testing or condition + branch coverage criteria. Reducing the complexity of such conditions by, for example, breaking them into multiple smaller…

  • The coupling of the class under test

    In a world of cohesive classes, we combine different classes to build large behaviors. But doing so may lead to a highly coupled design. Excessive coupling may harm evolution, as changes in one class may propagate to other classes in ways that are not clear. Therefore, we should strive for classes that are coupled as…

  • The cohesion of the class under test

    Cohesion is about a module, a class, a method, or any element in your architecture having only a single responsibility. Classes with multiple responsibilities are naturally more complex and harder to comprehend than classes with fewer responsibilities. So, strive for classes and methods that do one thing. Defining what a single responsibility means is tricky…

  • Designing for testability in the real world

    Writing tests offers a significant advantage during development: if you pay attention to them (or listen to them, as many developers say), they may give you hints about the design of the code you are testing. Achieving good class design is a challenge in complex object-oriented systems. The more help we get, the better. The buzz about…

  • Dependency via class constructor or value via method parameter?

    A very common design decision is whether to pass a dependency to the class via constructor (so the class uses the dependency to get a required value) or pass that value directly to the method. As always, there is no right or wrong way. However, there is a trade-off you must understand to make the…