Share
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
Transcript
41 slides
-
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 💻 -
Agenda 📖 1. Background 2. The liar 3. Excessive setup
4. The giant 5. The slow poke 6. Wrapping up -
When Test Driven Development Goes Wrong Dave Farley - By
Continuous Delivery When Test Driven Development Goes Wrong When Test Driven Development Goes Wrong -
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 -
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. -
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 -
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 -
Root cause 1. Lack of practice on TDD 2. Oriented
to coverage ‼ -
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. -
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 -
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 -
/** * 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 -
Root cause 1. Adding tests after the source code 2.
Lack of SOLID principes -
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. -
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 -
Root cause 1. Test after, instead of test first 2.
Lack of SOLID principes 3. Coupled with the XML lib -
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. -
test('should show Buggy on user interaction by keyboard', done =>
{ const wrapper = mount( <Guide 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 <Guide 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 <Guide 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 <Guide 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 <Guide 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 -
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. -
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. -
Testing is Good. Pyramids are Bad. Ice Cream Cones are
the Worst - Stephen H Fishman -
Working skeleton Growing object oriented software, guided by tests -
Steve Freeman and Nat Pryce -
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 💻