Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How tests simplify code

3771a70b46d12cb049e7be561ae69116?s=47 martinsson
November 15, 2018

How tests simplify code

Our tests are sometimes giving us pain. But if we listen carefully and have enough design options up our sleeve they can point us in the direction of production code simplification. Links to cognitive psychology.

3771a70b46d12cb049e7be561ae69116?s=128

martinsson

November 15, 2018
Tweet

Transcript

  1. Tests - simplificateur de code? Johan Martinsson @johan_alps changit.fr

  2. Exceptions! exceptionalMethod(userId, data) { try { let isOwner try {

    isOwner = this.authservice.isOwnerOf(userId, data.id) } catch (e) { this.logger.error('got http exception while invoking authService') } if (isOwner) { this.dao.update(data.id, data) } } catch (daoException) { this.logger.error('exception caught') } } nonExceptionalMethod(userId, data) { let isOwner = this.authservice.isOwnerOf(userId, data.id) if (isOwner) { this.dao.update(data.id, data) } }
  3. l’angoisse de null function printItemNames(data) { if (data.prop) { if

    (data.prop.cart) { if (data.prop.cart.getList() !== null && data.prop.cart.getList() !== undefined) { for (let listItem of data.prop.cart.getList()) { if (listItem.description) { if (listItem.description.name) { console.debug('Phew, finally here!') // actual business logic console.log('And the item name is... ' + listItem.description.name) } } } } } } } function printItemNames(data) { for (let listItem of data.prop.cart.getList()) { console.debug('Phew, finally here!') // actual business logic console.log('And the item name is... ' + listItem.description.name) } }
  4. Conditions function conditionalMethod(data) { if (data.something === 'val') { doSomething()

    if (data.age <= 40 || 'toto' !== data.something) { doAThirdThing() } else { doSomethingAdditional() } } }
  5. Conditions, quand on aime … function conditionalCode(data) { if (data.prop)

    { if (data.prop.service) { if (data.prop.service.getList() === null && data.prop.service.getList().size > 0) { return 'processed ' + data.prop.service.getList().length + ' results' } else { let defaultResult = 'waiting' return defaultResult; } } } }
  6. Duplication registerEmail(person) { if (person.age < 13) { throw new

    Error('too young') } if (person.name === null) { throw new Error('person must have name') } if (person.email.indexOf('@') <= 0) { throw new Error('invalid email') } console.log('do some stuff') this.dao.insertPerson(person) } registerMobile(person) { if (person.age < 13) { throw new Error('too young') } if (person.name === null) { throw new Error('person must have name') } if (this.hasNonDigits(person.phoneNumber)) { new Error('invalid phone number') } console.log('do some stuff') this.dao.insertPerson(person) } hasNonDigits(phoneNumber) { return /[^\d]+/.test(phoneNumber) }
  7. Arguments indirects updateCartItems() { let httpSession = HttpSession.getInstance() let rawCart

    = httpSession.getCookie().getValue('cart') let cart = new Cart(rawCart) if (cart.lastModificationDate < Date.now() - 24 * 60 * 60 * 1000) { cart.removeAllItems(); } // etc ... // heavy logic to make sure availability is still ok, // that prices haven't changed // that items in the cart haven't expired etc }
  8. Service injection, injection, injection … "Chaque nouvelle dépendance doit être

    passée sur toute la callstack" NOT Injection par constructeur - un seul endroit
  9. Code inspectable function hardToTest() { let data = getStuffFromWebservice() //

    // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // let result let saveResult = saveToDatabase(result) } function easyToTest() { let data = getStuffFromWebservice() let result = easyToTestPureFunction(data) let saveResult = saveToDatabase(result) } function easyToTestPureFunction(data) { // // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // pure logic // }
  10. Améliore-t-on le code?

  11. Avec tests on a la possibilité de simplifier le code

  12. Cercle vicieux/ vertueux

  13. None
  14. “Sometimes we find it difficult to write a test …

    this usually means that our design can be improved” - S. Freeman, N. Pryce “The tests are a canary in coal mining revealing by their distress the presence of evil design vapors” - Kent Beck
  15. None
  16. TDD requiert une maîtrise cognitive Etat préalable? Action? Résultat attendu?

  17. Chunking

  18. Mémoire court terme

  19. Mémoire long terme Gael Varoquaux flickr Attribution 2.0 Generic (CC

    BY 2.0)
  20. Notre cerveau a besoin de Blocs de 4-5 éléments noms

    cohésion assembler plutôt que modifier
  21. Notre code de prod a besoin de peu de duplication

    peux de couplage morceaux à assembler BugsZero
  22. Les tests ont besoin de être rapides Petites choses Responsabilités

    séparés Isolation des dépendances
  23. Laissons les tests nous aider dans la quête du bon

    design Ayons zéro tolérance pour des mauvais tests
  24. ? @johan_alps changit.fr Tom Mullen (cognitive psychology) Michael Feathers (synergy

    between testablity and good design)