braindump ... cause thread-dumps are not enough ;)

Notes on "Clean Code - Tests" chapter

  • Three laws of TDD:
    1. You may not write production code until you have written a failing unit test.
    2. You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
    3. You may not write more production code than is sufficient to pass the currently failing test.
  • How fast is TDD this way?
    • According to Uncle Bob, you can write over a dozen such tests each day.
    • On the other hand, a loop of writing test, running failing test, writing prod code, running passing test should take around 30 seconds.
  • Quick and dirty tests are as good as no tests at all. They end up as being hard to maintain and get quickly dropped when first problems occur.
  • “Test code is as important as production code.” - it needs to be maintained, refactored and requires and requires a lot of thought and attention.
  • Tests vastly enhance possibilities to work with the code - they enable introducing changes in a safe and controllable manner
  • Test code has a different set of engineering standards than the production code. It can be less performant - but needs to be simple, readable and cohesive.
  • Ideally there should be a single assertion per test. This is a good advice, however there is a better rule: test one concept per test case (answer a single question). Some questions require more than one assertion to be answered.
  • Five rules of unit tests: F.I.R.S.T:

    Fast - as fast as possible - ideally couple seconds - we want to run them as often as possible

    Independent - each test case should run alone; if a test fails - it fails because of its own specific reason - not because the setup test case failed

    Repeatable - should run in the same way in every environment

    Self-validating - they are automatically validated; test has binary output: passed or failed

    Timely - written in proper moment - before the production code

Notes on "Clean Code - Boundaries" chapter

  • Encapsulate foreign/alien APIs (create own interfaces and adapters using the foreign API).
  • Create “learning” unit tests - tests which verify that your usage of API is correct. “Learning” - because you actually learn the API by writing them. Such tests can be later used eg. to ensure that new version of framework/library behaves accordingly (that they are compatible with the way you use them).

Notes on "Clean Code - Error Handling" chapter

  • “Error handling is important, but if it obscures logic, it’s wrong”
  • Using exceptions make it possible to decouple business logic from error handling.
  • Using unchecked exceptions in Java is preferable. Checked exceptions violate Open/Closed principle. Example:
class A {
    void methodA() {}
}

class B {
    void methodB() {
        new A().methodA();
    }
}

class C {
    public static void main(String[] args]) {
        new B().methodB();
    }
}

If I want to add checked exception to methodA() I need to change signature iun methodB() - which shouldn’t care about it at all. Encapsulation is broken!

  • Provide context to exception which explains why the operation failed.
  • Sometimes it is required to handle couple different exception types coming from a single method call in the same way. In such case, it might be better to create a wrapper class, which will encapsulate that call and throw a single, common exception type (containing the original cause). This way the original invocation will need to handle only a single exception - this will reduce duplication.
  • Don’t return null values - return empty/guard values (Special Case Pattern).
  • Don’t pass null values as arguments.

jUnit creates new instance of test class for each test method

This seems to be a bit counter-intuitive. You can use setUp() and tearDown() methods to setup the test environment. On the other hand - the code can be a bit shorter and more concise. Consider those two fragments:

class SomeClassTest {

    private int veryImportantField;
    private long anotherVeryImportantField;
    
    @Before
    public void setUp() {
        veryImportantField = 10;
        anotherVeryImportantFieldi = 100L; 
    }

}

and

class SimplifiedTest {
    private int veryImportantField = 10;
    private long anotherVeryImportantField = 100L;
}

The latter is definitely cleaner and simpler to understand.

I have been using that trick for a while - but I’ve never found any actual evidence whether this is/isn’t a bug. It seemed that this behavior (even if introduced by accident) just sticked with jUnit and is not likely going to change. This is what I thought until today! While reading Martin Fowler’s blog I found this particular post. This was actually a design decision to make it this way. Long live short tests! :)

notes on "Clean Code - Objects and Data structures" chapter

  • Hiding implementation is about making abstractions and not putting layers of functions (like numerous getters and setters).
  • “Objects hide the data behind abstractions and expose functions that operate on that data.”
  • “Data structures just expose their data and have no meaningful functions.”
  • Procedural code makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions.
  • “Mature programmers know that the idea that everything is an object is a myth.”
  • The Law of Demeter
    Method f of class C should only call the methods of these:
    • C (itself)
    • An object created by f
    • An object passed as an argument to f
    • An object held in an instance variable of C
  • The method should not invoke methods on objects that are returned by the allowed functions!
  • If the things which are accessed within a method are data structures with no behavior the Law of Demeter do not apply.
  • Avoid creating hybrids (half object - half data structures) - classes which have meaningful methods and expose internal structure via getters/setters.
  • Refactoring note: Try to identify what exposed data is used for -> maybe it is possible to extract a method and move it to the exposing class?