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

TDD anti-patterns

Marabesi
August 26, 2021

TDD anti-patterns

Testing practices has increasing its adoption by developers, shifting left the test responsibilities and increasing the quality of the code, besides that, continuous testing is an agile practice that impacts the software development life cycle.

In this talk we are going to focus on the TDD anti-patterns, a list inspired by James Carr. From his list, those are the ones I consider the most popular anti-patterns: The liar, Excessive setup, The giant, The Slow Poke (The last one is one of my favorites, is it a pokemon?), beside those the list goes on and we will cover a few more.

TDD anti-patterns is a list to keep our test suite sharp and avoid those mistakes to keep it running fast and maintainable.

Remember, keep it fast, execute it often and keep testing!

Marabesi

August 26, 2021
Tweet

More Decks by Marabesi

Other Decks in Programming

Transcript

  1. ANTI-PATTERNS
    TDD

    View full-size slide

  2. Hello there, you can call me
    Marabesi
    But my name is, Matheus Marabesi
    Software craftsperson at

    Codurance The
    Software Craftsman
    TDD, XP, Design Patterns e Testable
    @MatheusMarabesi
    🐦
    marabesi.com
    💻

    View full-size slide

  3. Agenda
    📖
    1. Background
    2. The liar
    3. Excessive setup
    4. The giant
    5. The slow poke
    6. Wrapping up

    View full-size slide

  4. 1. Background

    View full-size slide

  5. When Test Driven Development Goes Wrong
    Dave Farley - By Continuous Delivery
    When Test Driven Development Goes Wrong
    When Test Driven Development Goes Wrong

    View full-size slide

  6. 22
    The Liar -
    The Giant -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    Excessive Setup -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    The Slow Poke
    The Mockery The Inspector Generous Leftovers The Local Hero
    The Nitpicker The Secret Catcher The Dodger The Loudmouth The Greedy Catcher
    The Sequencer Hidden Dependency The Enumerator The Stranger
    The Operating System Evangelist Success Against All Odds The Free Ride The One
    The Peeping Tom
    James Carr - TDD Anti-Patterns

    View full-size slide

  7. 2. The liar
    An entire unit test that passes all of the test cases it has
    and appears valid, but upon closer inspection it is
    discovered
    that it doesn’t really test the intended target at all.

    View full-size slide

  8. test('the data is peanut butter', () => {
    function callback(data) {
    expect(data).toBe('peanut butter');
    }
    fetchData(callback);
    });
    1
    2
    3
    4
    5
    6
    7
    function callback(data) {
    expect(data).toBe('peanut butter');
    }
    test('the data is peanut butter', () => {
    1
    2
    3
    4
    5
    fetchData(callback);
    6
    });
    7
    fetchData(callback);
    test('the data is peanut butter', () => {
    1
    function callback(data) {
    2
    expect(data).toBe('peanut butter');
    3
    }
    4
    5
    6
    });
    7
    test('the data is peanut butter', () => {
    function callback(data) {
    expect(data).toBe('peanut butter');
    }
    fetchData(callback);
    });
    1
    2
    3
    4
    5
    6
    7
    Jest docs - Callbacks

    View full-size slide

  9. test('the data is peanut butter', done => {
    function callback(data) {
    try {
    expect(data).toBe('peanut butter');
    done();
    } catch (error) {
    done(error);
    }
    }
    fetchData(callback);
    });
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    test('the data is peanut butter', done => {
    1
    function callback(data) {
    2
    try {
    3
    expect(data).toBe('peanut butter');
    4
    done();
    5
    } catch (error) {
    6
    done(error);
    7
    }
    8
    }
    9
    10
    fetchData(callback);
    11
    });
    12
    expect(data).toBe('peanut butter');
    done();
    test('the data is peanut butter', done => {
    1
    function callback(data) {
    2
    try {
    3
    4
    5
    } catch (error) {
    6
    done(error);
    7
    }
    8
    }
    9
    10
    fetchData(callback);
    11
    });
    12
    done(error);
    test('the data is peanut butter', done => {
    1
    function callback(data) {
    2
    try {
    3
    expect(data).toBe('peanut butter');
    4
    done();
    5
    } catch (error) {
    6
    7
    }
    8
    }
    9
    10
    fetchData(callback);
    11
    });
    12
    Jest docs - Callbacks

    View full-size slide

  10. Wow, async - watch out!
    Test without assertions

    View full-size slide

  11. Root cause
    1. Lack of practice on TDD
    2. Oriented to coverage ‼

    View full-size slide

  12. 3. Excessive setup
    A test that requires a lot of work setting up in order to even
    begin testing. Sometimes several hundred lines of
    code is used
    to setup the environment for one test, with several objects
    involved, which can make it difficult to
    really ascertain what
    is tested due to the “noise” of all of the setup going on.

    View full-size slide

  13. import path from 'path'
    import compression from 'compression'
    import connect from 'connect'
    import consola from 'consola'
    import serveStatic from 'serve-static'
    import servePlaceholder from 'serve-placeholder'
    import launchMiddleware from 'launch-editor-middleware'
    import { determineGlobals, isUrl } from '@nuxt/utils'
    import { VueRenderer } from '@nuxt/vue-renderer'
    import Server from '../src/server'
    import Listener from '../src/listener'
    import ServerContext from '../src/context'
    import renderAndGetWindow from '../src/jsdom'
    import nuxtMiddleware from '../src/middleware/nuxt'
    import errorMiddleware from '../src/middleware/error'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    import path from 'path'
    1
    import compression from 'compression'
    2
    import connect from 'connect'
    3
    import consola from 'consola'
    4
    import serveStatic from 'serve-static'
    5
    import servePlaceholder from 'serve-placeholder'
    6
    import launchMiddleware from 'launch-editor-middleware'
    7
    import { determineGlobals, isUrl } from '@nuxt/utils'
    8
    import { VueRenderer } from '@nuxt/vue-renderer'
    9
    10
    import Server from '../src/server'
    11
    import Listener from '../src/listener'
    12
    import ServerContext from '../src/context'
    13
    import renderAndGetWindow from '../src/jsdom'
    14
    import nuxtMiddleware from '../src/middleware/nuxt'
    15
    import errorMiddleware from '../src/middleware/error'
    16
    Nuxt.js server

    View full-size slide

  14. import { Component } from 'react';
    import PropTypes from 'prop-types';
    import { Redirect } from 'react-router';
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    import { track } from '../../../../packages/emitter/Tracking';
    import { auth } from '../../../../pages/login/Auth';
    import Reason from '../../../../packages/engine/Reason';
    import EditorManager from '../editor-manager/EditorManager';
    import Guide from '../guide/Guide';
    import Intro from '../intro/Intro';
    import DebugButton from '../../buttons/debug/Debug';
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    import {executeTestCase} from '../../../../packages/engine/Tester';
    const Wrapped = (
    code,
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const Wrapped = (
    code,
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    15
    16 code,
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    16
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    code,
    16
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    code,
    16
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    code,
    16
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    code,
    16
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    code,
    16
    import { Component } from 'react';
    1
    import PropTypes from 'prop-types';
    2
    import { Redirect } from 'react-router';
    3
    import Emitter, { PROGRESS_UP, LEVEL_UP } from '../../../../packages/emitter/Emitter
    4
    import { track } from '../../../../packages/emitter/Tracking';
    5
    import { auth } from '../../../../pages/login/Auth';
    6
    import Reason from '../../../../packages/engine/Reason';
    7
    import EditorManager from '../editor-manager/EditorManager';
    8
    import Guide from '../guide/Guide';
    9
    import Intro from '../intro/Intro';
    10
    import DebugButton from '../../buttons/debug/Debug';
    11
    import {SOURCE_CODE, TEST_CODE} from '../editor-manager/constants';
    12
    import {executeTestCase} from '../../../../packages/engine/Tester';
    13
    14
    const Wrapped = (
    15
    code,
    16
    Testable
    test case matchers

    View full-size slide

  15. /**
    * Makes sure the use of "localhost" in the Hudson URL reports a warning.
    */
    @Test
    public void localhostWarning() throws Exception {
    HtmlPage p = j.createWebClient().goTo("configure");
    HtmlInput url = p.getFormByName("config").getInputByName("_.url");
    url.setValueAttribute("http://localhost:1234/");
    assertThat(p.getDocumentElement().getTextContent(), containsString("instead of l
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    HtmlPage p = j.createWebClient().goTo("configure");
    /**
    1
    * Makes sure the use of "localhost" in the Hudson URL reports a warning.
    2
    */
    3
    @Test
    4
    public void localhostWarning() throws Exception {
    5
    6
    HtmlInput url = p.getFormByName("config").getInputByName("_.url");
    7
    url.setValueAttribute("http://localhost:1234/");
    8
    assertThat(p.getDocumentElement().getTextContent(), containsString("instead of l
    9
    }
    10
    HtmlInput url = p.getFormByName("config").getInputByName("_.url");
    /**
    1
    * Makes sure the use of "localhost" in the Hudson URL reports a warning.
    2
    */
    3
    @Test
    4
    public void localhostWarning() throws Exception {
    5
    HtmlPage p = j.createWebClient().goTo("configure");
    6
    7
    url.setValueAttribute("http://localhost:1234/");
    8
    assertThat(p.getDocumentElement().getTextContent(), containsString("instead of l
    9
    }
    10
    url.setValueAttribute("http://localhost:1234/");
    /**
    1
    * Makes sure the use of "localhost" in the Hudson URL reports a warning.
    2
    */
    3
    @Test
    4
    public void localhostWarning() throws Exception {
    5
    HtmlPage p = j.createWebClient().goTo("configure");
    6
    HtmlInput url = p.getFormByName("config").getInputByName("_.url");
    7
    8
    assertThat(p.getDocumentElement().getTextContent(), containsString("instead of l
    9
    }
    10
    assertThat(p.getDocumentElement().getTextContent(), containsString("instead of l
    /**
    1
    * Makes sure the use of "localhost" in the Hudson URL reports a warning.
    2
    */
    3
    @Test
    4
    public void localhostWarning() throws Exception {
    5
    HtmlPage p = j.createWebClient().goTo("configure");
    6
    HtmlInput url = p.getFormByName("config").getInputByName("_.url");
    7
    url.setValueAttribute("http://localhost:1234/");
    8
    9
    }
    10
    Jenkins configuration location

    View full-size slide

  16. Root cause
    1. Adding tests after the source code
    2. Lack of SOLID principes

    View full-size slide

  17. 4. The giant
    A unit test that, although it is validly testing the object under test, can span thousands
    of lines and contain many
    many test cases. This can be an indicator
    that the system under tests is a God Object.

    View full-size slide

  18. namespace Sped\Gnre\Sefaz;
    use Sped\Gnre\Sefaz\LoteGnre;
    use Sped\Gnre\Sefaz\EstadoFactory;
    class Lote extends LoteGnre
    {
    private $estadoFactory;
    private $ambienteDeTeste = false;
    public function getEstadoFactory()
    {...}
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public function getEstadoFactory()
    {...}
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    13
    14
    15
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    namespace Sped\Gnre\Sefaz;
    1
    2
    use Sped\Gnre\Sefaz\LoteGnre;
    3
    use Sped\Gnre\Sefaz\EstadoFactory;
    4
    5
    class Lote extends LoteGnre
    6
    {
    7
    8
    private $estadoFactory;
    9
    10
    private $ambienteDeTeste = false;
    11
    12
    public function getEstadoFactory()
    13
    {...}
    14
    15
    public function setEstadoFactory(EstadoFactory $estadoFactory)
    16
    sped GNRE

    View full-size slide

  19. Root cause
    1. Test after, instead of test first
    2. Lack of SOLID principes
    3. Coupled with the XML lib

    View full-size slide

  20. 5. The Slow Poke
    A unit test that runs incredibly slow. When developers kick it off,
    they have time to go to the bathroom, grab a
    smoke, or worse,
    kick the test off before they go home at the end of the day.

    View full-size slide

  21. Eradicating Non-Determinism in Tests - Asynchronous Behavior
    Martin Fowler, 2011

    View full-size slide

  22. test('should show Buggy on user interaction by keyboard', done => {
    const wrapper = mount(
    guideContent={content}
    currentHint={0}
    showNext={false}
    invalidCode={false}
    afkExpirationTime={400}
    />
    );
    setTimeout(() => {
    wrapper.update();
    expect(wrapper.find('BuggySleepy').length).toBe(1);
    const keypress = new KeyboardEvent('keydown', {keyCode: 37});
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    setTimeout(() => {
    wrapper.update();
    expect(wrapper.find('BuggySleepy').length).toBe(1);
    const keypress = new KeyboardEvent('keydown', {keyCode: 37});
    test('should show Buggy on user interaction by keyboard', done => {
    1
    const wrapper = mount(
    2
    3
    guideContent={content}
    4
    currentHint={0}
    5
    showNext={false}
    6
    invalidCode={false}
    7
    afkExpirationTime={400}
    8
    />
    9
    );
    10
    11
    12
    13
    14
    15
    16
    test('should show Buggy on user interaction by keyboard', done => {
    1
    const wrapper = mount(
    2
    3
    guideContent={content}
    4
    currentHint={0}
    5
    showNext={false}
    6
    invalidCode={false}
    7
    afkExpirationTime={400}
    8
    />
    9
    );
    10
    11
    setTimeout(() => {
    12
    wrapper.update();
    13
    expect(wrapper.find('BuggySleepy').length).toBe(1);
    14
    15
    const keypress = new KeyboardEvent('keydown', {keyCode: 37});
    16
    wrapper.update();
    expect(wrapper.find('BuggySleepy').length).toBe(1);
    const keypress = new KeyboardEvent('keydown', {keyCode: 37});
    test('should show Buggy on user interaction by keyboard', done => {
    1
    const wrapper = mount(
    2
    3
    guideContent={content}
    4
    currentHint={0}
    5
    showNext={false}
    6
    invalidCode={false}
    7
    afkExpirationTime={400}
    8
    />
    9
    );
    10
    11
    setTimeout(() => {
    12
    13
    14
    15
    16 const keypress = new KeyboardEvent('keydown', {keyCode: 37});
    test('should show Buggy on user interaction by keyboard', done => {
    1
    const wrapper = mount(
    2
    3
    guideContent={content}
    4
    currentHint={0}
    5
    showNext={false}
    6
    invalidCode={false}
    7
    afkExpirationTime={400}
    8
    />
    9
    );
    10
    11
    setTimeout(() => {
    12
    wrapper.update();
    13
    expect(wrapper.find('BuggySleepy').length).toBe(1);
    14
    15
    16

    View full-size slide

  23. CRON jobs
    Leap year / any time related

    View full-size slide

  24. I can also relate this pattern to the following issues:
    1. Focus more on integration test instead of unit, this can
    lead to
    slow suites. This is also a side effect of
    focusing too munch on
    coverage instead of side effect.

    View full-size slide

  25. 2. Trying to reverse engineer the TDD flow. it often happens when
    there is no tests on the code
    and as soon as developers start to add
    them, it hits the point 1 back.

    View full-size slide

  26. Testing is Good. Pyramids are Bad. Ice Cream Cones are the Worst - Stephen H Fishman

    View full-size slide

  27. 6. Wrapping up

    View full-size slide

  28. The liar
    Excessive setup
    The giant
    The slow poke
    and many more!

    View full-size slide

  29. Most of the anti-patterns presented are
    related to test last

    View full-size slide

  30. If its hard to test, take a step back.

    View full-size slide

  31. Working
    skeleton
    Growing object oriented software, guided by tests - Steve Freeman and Nat Pryce

    View full-size slide

  32. Happy testing
    🤪

    View full-size slide

  33. Hello there, you can call me
    Marabesi
    But my name is, Matheus Marabesi
    Software craftsperson at

    TDD, XP, Design Patterns and
    Codurance The
    Software Craftsman
    Testable
    @MatheusMarabesi
    🐦
    marabesi.com
    💻

    View full-size slide