Slide 1

Slide 1 text

ANTI-PATTERNS TDD

Slide 2

Slide 2 text

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 💻

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

1. Background

Slide 5

Slide 5 text

T.D.D

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

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.

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Wow, async - watch out! Test without assertions

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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.

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

/** * 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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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.

Slide 27

Slide 27 text

via GIPHY

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

test('should show Buggy on user interaction by keyboard', done => { const wrapper = mount( ); 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 9 ); 10 11 12 13 14 15 16 test('should show Buggy on user interaction by keyboard', done => { 1 const wrapper = mount( 2 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 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 9 ); 10 11 setTimeout(() => { 12 wrapper.update(); 13 expect(wrapper.find('BuggySleepy').length).toBe(1); 14 15 16

Slide 30

Slide 30 text

CRON jobs Leap year / any time related

Slide 31

Slide 31 text

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.

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

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.

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

6. Wrapping up

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

If its hard to test, take a step back.

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Happy testing 🤪

Slide 41

Slide 41 text

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 💻