Joel over Test Driven Development

In de podcast waar o.a. Kent Beck niet zo blij mee was, heeft Joel Spolsky een voorbeeld van een stukje code waar het meer moeite kost om een test eerst te schrijven, dan dat het wat oplevert.

Het gaat over het aanpassen van de jpeg compressie in een stukje remote control software via een toolbar button. Wat hij als test ziet is de actie uitvoeren en het resultaat terug krijgen van een remote machine en dan controleren of twee images gelijk zijn. Veel werk om te automatiseren, maar in mijn vocabulaire heet dit dan ook een integratie test.

Hij zegt zelf dat het enige dat hij hoeft te coderen is dat de button een een bepaalde waarde als parameter mee gaat geven. Ik neem aan dat het geen extra if statement is in een procedure van 100 regels die alles doet, dus begrijp ik niet wat er zo moeilijk aan is om dit te unit testen. Een test hiervoor heb ik ook in om en bij vijf regels code geschreven, afhankelijk van taal en test framework.

En als er toch zo een high coupling is tussen de modules wat het moeilijk maakt, dan is het juist TDD dat ervoor zorgt je de code wat beter modulair opzet. Dat je een betere scheiding krijgt tussen gui en backend. Een andere keer had hij het namelijk ook over een incident waar het veranderen van de menustructuur allerlei testen kapot maakten. Ja dat zijn automatische testen, maar ik durf te wedden dat ze achteraf geschreven zijn. En in dat geval dus niks met het TDD concept te maken.

Anyway, je moet altijd kritisch lezen en luisteren, maar als het gaat om meningen van Joel over zaken die uit de Agile hoek komen, ben ik altijd extra alert.

Utility classes en default constructors

Checkstyle heeft standaard een warning als je een utility class, een class met alleen static methods, hebt waarvan je een nutteloze instantie kan maken. De oplossing is dan om een private default constructor te maken. Ja, java kent geen modules dus de "nette" manier om van een class een module te maken is door er een stukje "unreachable" code in te plaatsen. Alsof je niet al voldoende code moet lezen in java.

Anyway, ik doe daar niet aan mee. Ik ga ervan uit dat degene die mijn utility class gebruikt, capabel genoeg is om te zien dat een instantie ervan nutteloos is. Ik bedoel, wat kan er fout gaan? Je maakt een instantie, en vervolgens merk je dat je er niks mee kan. Voordat je op de compile knop drukt heb je de code al weggehaald en de kans dat het in productie code komt is nihil. Het beschermen van de programmeur tegen zichzelf heeft java al lastig genoeg gemaakt (zoals checked exceptions).

Wat nog wel een ander probleem was met een private constructor voor een utility class, is dat cobertura wel de constructor ziet en vind dat deze niet getest is. Je Code Coverage er mee omlaag gaat, en als je op 100% zit is het zonde om dan weer terug te gaan naar 99%. Dan valt de echte niet geteste code minder op.

Maar als je toch de private constructor wilt gebruiken kun je met reflectie toch nog op 100% code coverage komen.

1
2
3
4
5
6
7
8
9
10
11
12
public void testPrivateConstructor() throws
         IllegalAccessException,
         InvocationTargetException,
         InstantiationException {
 
    final Class<?> aClass = Environment.class;
    final Constructor<?> constructor = aClass.getDeclaredConstructors()[0];
    constructor.setAccessible(true);
    final Object n = constructor.newInstance((Object[]) null);
    assertEquals("should have environment instance", 
                          Environment.class, n.getClass());        
}

Of je zet nog meer onzin code in de productie code door een static initializer toe te passen:

1
2
3
{
   new Environment();
}