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

TDD anti-patterns at Codurance Spain

Ed39ca0d44a6e6cdefc76ac548de5f41?s=47 Marabesi
October 14, 2021

TDD anti-patterns at Codurance Spain

Video available at https://app.livestorm.co/codurance/testing-anti-patterns-workshop

Testing practices have increased 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 Slow Poke.

Besides that, we are going to dive into what they are and how to avoid them to keep your test suite sharp.

In this session the focus will be on the theory part of TDD and the anti-patterns to avoid. This talk assumes that the audience already has some knowledge on TDD and wants to improve their craft. The format will use code snippets to explore each anti-pattern.

https://www.meetup.com/pt-BR/codurance-craft-events/events/280794032

https://marabesi.com/tdd/2021/08/28/tdd-anti-patterns.html

Ed39ca0d44a6e6cdefc76ac548de5f41?s=128

Marabesi

October 14, 2021
Tweet

Transcript

  1. TDD codurance.com Testing anti-patterns

  2. Matheus Marabesi Hello there, you can call me Marabesi, But

    my name is Matheus Marabesi, I work at Codurance as a Software craftsperson. I enjoy talking about anything related to: testing, patterns and gamification. You can find me at @MatheusMarabesi or https://marabesi.com Codurance Crafting Code
  3. 1. Intro - Background 2. Excessive setup 3. The Liar

    4. The giant 5. The slow poke 6. Wrapping up Crafting code Agenda
  4. 1. Background Warming up Getting started

  5. TDD

  6. None
  7. When Test Driven Development Goes Wrong Dave Farley - by

    Continuous Delivery
  8. tddconf.com

  9. The Liar The Giant The Mockery The Inspector Generous Leftovers

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

    Generous Leftovers The Local Hero The Nitpicker The Secret Catcher The Dodger The Loudmouth Anti patterns The Greedy Catcher Excessive Setup 3 The Sequencer Hidden Dependency The Enumerator The Stranger The Operating System Evangelist Success Against All Odds The Free Ride The One The Peeping Tom The Slow Poke 6
  11. SSD 14/16: Test Patterns and Anti-Patterns Yegor Bugayenko

  12. None
  13. None
  14. None
  15. None
  16. 2. Excessive setup - 🏆3 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. Crafting code
  17. None
  18. jest.mock('compression') jest.mock('connect') jest.mock('serve-static') jest.mock('serve-placeholder') jest.mock('launch-editor-middleware') jest.mock('@nuxt/utils') jest.mock('@nuxt/vue-renderer') jest.mock('../src/listener') jest.mock('../src/context') jest.mock('../src/jsdom')

    jest.mock('../src/middleware/nuxt') jest.mock('../src/middleware/error') jest.mock('../src/middleware/timing') Jest - javascript - Nuxtjs
  19. describe('server: server', () => { beforeAll(() => { jest.spyOn(path, 'join').mockImplementation((...args)

    => `join(${args.join(', ')})`) jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`) connect.mockReturnValue({ use: jest.fn() }) serveStatic.mockImplementation(dir => ({ id: 'test-serve-static', dir })) nuxtMiddleware.mockImplementation(options => ({ id: 'test-nuxt-middleware', ...options })) errorMiddleware.mockImplementation(options => ({ id: 'test-error-middleware', ...options })) createTimingMiddleware.mockImplementation(options => ({ id: 'test-timing-middleware', ...options })) // piece of code hidden }) Jest - javascript Jest - javascript - Nuxtjs
  20. describe('server: server', () => { beforeAll(() => { jest.spyOn(path, 'join').mockImplementation((...args)

    => `join(${args.join(', ')})`) jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`) connect.mockReturnValue({ use: jest.fn() }) serveStatic.mockImplementation(dir => ({ id: 'test-serve-static', dir })) nuxtMiddleware.mockImplementation(options => ({ id: 'test-nuxt-middleware', ...options })) errorMiddleware.mockImplementation(options => ({ id: 'test-error-middleware', ...options })) createTimingMiddleware.mockImplementation(options => ({ id: 'test-timing-middleware', ...options })) // piece of code hidden }) Jest - javascript Jest - javascript - Nuxtjs
  21. describe('server: server', () => { beforeAll(() => { jest.spyOn(path, 'join').mockImplementation((...args)

    => `join(${args.join(', ')})`) jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`) connect.mockReturnValue({ use: jest.fn() }) serveStatic.mockImplementation(dir => ({ id: 'test-serve-static', dir })) nuxtMiddleware.mockImplementation(options => ({ id: 'test-nuxt-middleware', ...options })) errorMiddleware.mockImplementation(options => ({ id: 'test-error-middleware', ...options })) createTimingMiddleware.mockImplementation(options => ({ id: 'test-timing-middleware', ...options })) // piece of code hidden }) Jest - javascript Jest - javascript - Nuxtjs
  22. test('should construct server', () => { const nuxt = createNuxt()

    // piece of code hidden let server = new Server(nuxt) expect(server.nuxt.hook).toBeCalledWith('close', expect.any(Function)) // piece of code hidden const closeHook = server.nuxt.hook.mock.calls[0][1] server.close = jest.fn() expect(server.close).not.toBeCalled() closeHook() expect(server.close).toBeCalledTimes(1) // piece of code hidden server = new Server(nuxt) expect(server.publicPath).toBe(nuxt.options.build._publicPath) }) Jest - javascript - Nuxtjs
  23. test('should construct server', () => { const nuxt = createNuxt()

    // piece of code hidden let server = new Server(nuxt) expect(server.nuxt.hook).toBeCalledWith('close', expect.any(Function)) // piece of code hidden const closeHook = server.nuxt.hook.mock.calls[0][1] server.close = jest.fn() expect(server.close).not.toBeCalled() closeHook() expect(server.close).toBeCalledTimes(1) // piece of code hidden server = new Server(nuxt) expect(server.publicPath).toBe(nuxt.options.build._publicPath) }) Jest - javascript - Nuxtjs
  24. None
  25. None
  26. const Wrapped = ( code, test, testCaseTests, sourceCodeTests, guideContent, whenDoneRedirectTo,

    waitCodeToBeExecutedOnStep, enableEditorOnStep, trackSection, testCaseStrategy, sourceCodeStrategy, disableEditor, introContent, enableIntroOnStep, editorOptions, attentionAnimationTo = [] ) => { } Jest - Javascript - Reactjs
  27. const Wrapped = ( code, test, testCaseTests, sourceCodeTests, guideContent, whenDoneRedirectTo,

    waitCodeToBeExecutedOnStep, enableEditorOnStep, trackSection, testCaseStrategy, sourceCodeStrategy, disableEditor, introContent, enableIntroOnStep, editorOptions, attentionAnimationTo = [] ) => { } Jest - Javascript - Reactjs
  28. test('should accept editor options by parameter', () => { const

    HoC = Rocket( null, null, null, null, null, null, null, 'my-section', null, null, null, null, { [SOURCE_CODE]: { readOnly: true }, [TEST_CODE]: {} } ); const wrapper = shallow(<HoC />); expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true); expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true); }); Jest - Javascript - Reactjs
  29. test('should accept editor options by parameter', () => { const

    HoC = Rocket( null, null, null, null, null, null, null, 'my-section', null, null, null, null, { [SOURCE_CODE]: { readOnly: true }, [TEST_CODE]: {} } ); const wrapper = shallow(<HoC />); expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true); expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true); }); Jest - Javascript - Reactjs
  30. test('should accept editor options by parameter', () => { const

    HoC = Rocket( null, null, null, null, null, null, null, 'my-section', null, null, null, null, { [SOURCE_CODE]: { readOnly: true }, [TEST_CODE]: {} } ); const wrapper = shallow(<HoC />); expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true); expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true); }); Jest - Javascript - Reactjs
  31. test('should accept editor options by parameter', () => { const

    HoC = Rocket( null, null, null, null, null, null, null, 'my-section', null, null, null, null, { [SOURCE_CODE]: { readOnly: true }, [TEST_CODE]: {} } ); const wrapper = shallow(<HoC />); expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true); expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true); }); Jest - Javascript - Reactjs
  32. The automation server

  33. /** * 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 localhost") ); } Junit - Java - Jenkins
  34. /** * 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 localhost") ); } Junit - Java Junit - Java - Jenkins
  35. /** * 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 localhost") ); } Junit - Java Junit - Java - Jenkins
  36. /** * 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 localhost") ); } Junit - Java Junit - Java - Jenkins
  37. /** * 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 localhost") ); } Junit - Java Junit - Java - Jenkins
  38. /** * 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 localhost") ); } Junit - Java Junit - Java - Jenkins
  39. • Adding tests after the source code • Lack of

    SOLID principles • Object Calisthenics? Root cause
  40. 3. The liar - 🏆4 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. Crafting code
  41. test('the data is peanut butter', () => { function callback(data)

    { expect(data).toBe('peanut butter'); } fetchData(callback); }); Jest - javascript
  42. test('the data is peanut butter', () => { function callback(data)

    { expect(data).toBe('peanut butter'); } fetchData(callback); }); Jest - javascript
  43. test('the data is peanut butter', () => { function callback(data)

    { expect(data).toBe('peanut butter'); } fetchData(callback); }); Jest - javascript
  44. test('the data is peanut butter', done => { function callback(data)

    { try { expect(data).toBe('peanut butter'); done(); } catch (error) { done(error); } } fetchData(callback); }); Jest - javascript
  45. test('the data is peanut butter', done => { function callback(data)

    { try { expect(data).toBe('peanut butter'); done(); } catch (error) { done(error); } } fetchData(callback); }); Jest - javascript
  46. test('the data is peanut butter', done => { function callback(data)

    { try { expect(data).toBe('peanut butter'); done(); } catch (error) { done(error); } } fetchData(callback); }); Jest - javascript
  47. test('the data is peanut butter', done => { function callback(data)

    { try { expect(data).toBe('peanut butter'); done(); } catch (error) { done(error); } } fetchData(callback); }); Jest - javascript
  48. test('the data is peanut butter', done => { function callback(data)

    { try { expect(data).toBe('peanut butter'); done(); } catch (error) { done(error); } } fetchData(callback); }); Jest - javascript
  49. None
  50. @Test fun `update user`() { val userId = 999 service.updateUserCustomAttributes(

    userId, mapOf(UserCustomAttribute.JOB to "MANAGER") ) } @Test fun `delete user`() { val userId = UserId.fromUuidString("e080-c8a-41bb-8ef-415124637e99") service.deleteUser(userId) } Junit - kotlin
  51. @Test fun `update user`() { val userId = 999 service.updateUserCustomAttributes(

    userId, mapOf(UserCustomAttribute.JOB to "MANAGER") ) } @Test fun `delete user`() { val userId = UserId.fromUuidString("e080-c8a-41bb-8ef-415124637e99") service.deleteUser(userId) } Junit - kotlin
  52. @Test fun `update user`() { val userId = 999 service.updateUserCustomAttributes(

    userId, mapOf(UserCustomAttribute.JOB to "MANAGER") ) } @Test fun `delete user`() { val userId = UserId.fromUuidString("e080-c8a-41bb-8ef-415124637e99") service.deleteUser(userId) } Junit - kotlin
  53. • Wow, async - watch out! • Test without assertions

    Points of attention
  54. • Lack of practice on TDD • Oriented to coverage

    Root cause
  55. 4. The giant - 🏆5 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. Crafting code
  56. namespace PhpOffice\PhpWord\Shared; class ConverterTest extends \PHPUnit\Framework\TestCase { public function testUnitConversions()

    { $values = array(); $values[] = 0; // zero value $values[] = rand(1, 100) / 100; // fraction number $values[] = rand(1, 100); // integer foreach ($values as $value) { $result = Converter::cmToTwip($value); $this->assertEquals($value / 2.54 * 1440, $result); $result = Converter::cmToInch($value); $this->assertEquals($value / 2.54, $result); PHPUnit - PHPOffice/Word
  57. namespace PhpOffice\PhpWord\Shared; class ConverterTest extends \PHPUnit\Framework\TestCase { public function testUnitConversions()

    { $values = array(); $values[] = 0; // zero value $values[] = rand(1, 100) / 100; // fraction number $values[] = rand(1, 100); // integer foreach ($values as $value) { $result = Converter::cmToTwip($value); $this->assertEquals($value / 2.54 * 1440, $result); $result = Converter::cmToInch($value); $this->assertEquals($value / 2.54, $result); PHPUnit - PHPOffice/Word
  58. namespace PhpOffice\PhpWord\Shared; class ConverterTest extends \PHPUnit\Framework\TestCase { public function testUnitConversions()

    { $values = array(); $values[] = 0; // zero value $values[] = rand(1, 100) / 100; // fraction number $values[] = rand(1, 100); // integer foreach ($values as $value) { $result = Converter::cmToTwip($value); $this->assertEquals($value / 2.54 * 1440, $result); $result = Converter::cmToInch($value); $this->assertEquals($value / 2.54, $result); PHPUnit - PHPOffice/Word
  59. namespace PhpOffice\PhpWord\Shared; class ConverterTest extends \PHPUnit\Framework\TestCase { public function testUnitConversions()

    { $values = array(); $values[] = 0; // zero value $values[] = rand(1, 100) / 100; // fraction number $values[] = rand(1, 100); // integer foreach ($values as $value) { $result = Converter::cmToTwip($value); $this->assertEquals($value / 2.54 * 1440, $result); $result = Converter::cmToInch($value); $this->assertEquals($value / 2.54, $result); PHPUnit - PHPOffice/Word
  60. namespace PhpOffice\PhpWord\Shared; class ConverterTest extends \PHPUnit\Framework\TestCase { public function testUnitConversions()

    { $values = array(); $values[] = 0; // zero value $values[] = rand(1, 100) / 100; // fraction number $values[] = rand(1, 100); // integer foreach ($values as $value) { $result = Converter::cmToTwip($value); $this->assertEquals($value / 2.54 * 1440, $result); $result = Converter::cmToInch($value); $this->assertEquals($value / 2.54, $result); PHPUnit - PHPOffice/Word
  61. $result = Converter::cmToPixel($value); $this->assertEquals($value / 2.54 * 96, $result); $result

    = Converter::cmToPoint($value); $this->assertEquals($value / 2.54 * 72, $result); $result = Converter::cmToEmu($value); $this->assertEquals(round($value / 2.54 * 96 * 9525), $result); $result = Converter::inchToTwip($value); $this->assertEquals($value * 1440, $result); $result = Converter::inchToCm($value); $this->assertEquals($value * 2.54, $result); $result = Converter::inchToPixel($value); $this->assertEquals($value * 96, $result); PHPUnit - PHPOffice/Word
  62. $result = Converter::cmToPixel($value); $this->assertEquals($value / 2.54 * 96, $result); $result

    = Converter::cmToPoint($value); $this->assertEquals($value / 2.54 * 72, $result); $result = Converter::cmToEmu($value); $this->assertEquals(round($value / 2.54 * 96 * 9525), $result); $result = Converter::inchToTwip($value); $this->assertEquals($value * 1440, $result); $result = Converter::inchToCm($value); $this->assertEquals($value * 2.54, $result); $result = Converter::inchToPixel($value); $this->assertEquals($value * 96, $result); PHPUnit - PHPOffice/Word
  63. $result = Converter::cmToPixel($value); $this->assertEquals($value / 2.54 * 96, $result); $result

    = Converter::cmToPoint($value); $this->assertEquals($value / 2.54 * 72, $result); $result = Converter::cmToEmu($value); $this->assertEquals(round($value / 2.54 * 96 * 9525), $result); $result = Converter::inchToTwip($value); $this->assertEquals($value * 1440, $result); $result = Converter::inchToCm($value); $this->assertEquals($value * 2.54, $result); $result = Converter::inchToPixel($value); $this->assertEquals($value * 96, $result); PHPUnit - PHPOffice/Word
  64. $result = Converter::cmToPixel($value); $this->assertEquals($value / 2.54 * 96, $result); $result

    = Converter::cmToPoint($value); $this->assertEquals($value / 2.54 * 72, $result); $result = Converter::cmToEmu($value); $this->assertEquals(round($value / 2.54 * 96 * 9525), $result); $result = Converter::inchToTwip($value); $this->assertEquals($value * 1440, $result); $result = Converter::inchToCm($value); $this->assertEquals($value * 2.54, $result); $result = Converter::inchToPixel($value); $this->assertEquals($value * 96, $result); PHPUnit - PHPOffice/Word
  65. $result = Converter::cmToPixel($value); $this->assertEquals($value / 2.54 * 96, $result); $result

    = Converter::cmToPoint($value); $this->assertEquals($value / 2.54 * 72, $result); $result = Converter::cmToEmu($value); $this->assertEquals(round($value / 2.54 * 96 * 9525), $result); $result = Converter::inchToTwip($value); $this->assertEquals($value * 1440, $result); $result = Converter::inchToCm($value); $this->assertEquals($value * 2.54, $result); $result = Converter::inchToPixel($value); $this->assertEquals($value * 96, $result); PHPUnit - PHPOffice/Word
  66. $result = Converter::cmToPixel($value); $this->assertEquals($value / 2.54 * 96, $result); $result

    = Converter::cmToPoint($value); $this->assertEquals($value / 2.54 * 72, $result); $result = Converter::cmToEmu($value); $this->assertEquals(round($value / 2.54 * 96 * 9525), $result); $result = Converter::inchToTwip($value); $this->assertEquals($value * 1440, $result); $result = Converter::inchToCm($value); $this->assertEquals($value * 2.54, $result); $result = Converter::inchToPixel($value); $this->assertEquals($value * 96, $result); PHPUnit - PHPOffice/Word
  67. None
  68. $result = Converter::inchToPoint($value); $this->assertEquals($value * 72, $result); $result = Converter::inchToEmu($value);

    $this->assertEquals(round($value * 96 * 9525), $result); $result = Converter::pixelToTwip($value); $this->assertEquals($value / 96 * 1440, $result); $result = Converter::pixelToCm($value); $this->assertEquals($value / 96 * 2.54, $result); $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); PHPUnit - PHPOffice/Word
  69. $result = Converter::inchToPoint($value); $this->assertEquals($value * 72, $result); $result = Converter::inchToEmu($value);

    $this->assertEquals(round($value * 96 * 9525), $result); $result = Converter::pixelToTwip($value); $this->assertEquals($value / 96 * 1440, $result); $result = Converter::pixelToCm($value); $this->assertEquals($value / 96 * 2.54, $result); $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); PHPUnit - PHPOffice/Word
  70. $result = Converter::inchToPoint($value); $this->assertEquals($value * 72, $result); $result = Converter::inchToEmu($value);

    $this->assertEquals(round($value * 96 * 9525), $result); $result = Converter::pixelToTwip($value); $this->assertEquals($value / 96 * 1440, $result); $result = Converter::pixelToCm($value); $this->assertEquals($value / 96 * 2.54, $result); $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); PHPUnit - PHPOffice/Word
  71. $result = Converter::inchToPoint($value); $this->assertEquals($value * 72, $result); $result = Converter::inchToEmu($value);

    $this->assertEquals(round($value * 96 * 9525), $result); $result = Converter::pixelToTwip($value); $this->assertEquals($value / 96 * 1440, $result); $result = Converter::pixelToCm($value); $this->assertEquals($value / 96 * 2.54, $result); $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); PHPUnit - PHPOffice/Word
  72. $result = Converter::inchToPoint($value); $this->assertEquals($value * 72, $result); $result = Converter::inchToEmu($value);

    $this->assertEquals(round($value * 96 * 9525), $result); $result = Converter::pixelToTwip($value); $this->assertEquals($value / 96 * 1440, $result); $result = Converter::pixelToCm($value); $this->assertEquals($value / 96 * 2.54, $result); $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); PHPUnit - PHPOffice/Word
  73. $result = Converter::inchToPoint($value); $this->assertEquals($value * 72, $result); $result = Converter::inchToEmu($value);

    $this->assertEquals(round($value * 96 * 9525), $result); $result = Converter::pixelToTwip($value); $this->assertEquals($value / 96 * 1440, $result); $result = Converter::pixelToCm($value); $this->assertEquals($value / 96 * 2.54, $result); $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); PHPUnit - PHPOffice/Word
  74. $result = Converter::degreeToAngle($value); $this->assertEquals((int) round($value * 60000), $result); $result =

    Converter::angleToDegree($value); $this->assertEquals(round($value / 60000), $result); } } PHPUnit - PHPOffice/Word
  75. $result = Converter::degreeToAngle($value); $this->assertEquals((int) round($value * 60000), $result); $result =

    Converter::angleToDegree($value); $this->assertEquals(round($value / 60000), $result); } } PHPUnit - PHPOffice/Word
  76. • How many test cases do I have? • Lacks

    feedback from the test • Data provider / Parameterized tests Points of attention
  77. • Test after, instead of test first Root cause

  78. 5. The slow poke - 🏆6 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. Crafting code
  79. Slow poke?

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

  81. None
  82. None
  83. 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}); document.dispatchEvent(keypress); Jest - Javascript - Reactjs
  84. 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}); document.dispatchEvent(keypress); Jest - Javascript - Reactjs
  85. setTimeout(() => { wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(1); const keypress = new KeyboardEvent('keydown',

    {keyCode: 37}); document.dispatchEvent(keypress); wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(0); done(); }, 500); }); Jest - Javascript - Reactjs
  86. setTimeout(() => { wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(1); const keypress = new KeyboardEvent('keydown',

    {keyCode: 37}); document.dispatchEvent(keypress); wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(0); done(); }, 500); }); Jest - Javascript - Reactjs
  87. setTimeout(() => { wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(1); const keypress = new KeyboardEvent('keydown',

    {keyCode: 37}); document.dispatchEvent(keypress); wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(0); done(); }, 500); }); Jest - Javascript - Reactjs
  88. setTimeout(() => { wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(1); const keypress = new KeyboardEvent('keydown',

    {keyCode: 37}); document.dispatchEvent(keypress); wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(0); done(); }, 500); }); Jest - Javascript - Reactjs
  89. setTimeout(() => { wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(1); const keypress = new KeyboardEvent('keydown',

    {keyCode: 37}); document.dispatchEvent(keypress); wrapper.update(); expect(wrapper.find('BuggySleepy').length).toBe(0); done(); }, 500); }); Jest - Javascript - Reactjs
  90. • CRON jobs • Leap year / any time related

    • Lack of control over non-determinism Points of attention
  91. Focus more on integration test instead of unit, this can

    lead to slow suites. This also can happen when focusing too much on coverage instead of having it as a side effect. Points of attention
  92. None
  93. 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 goes back to integration test and coverage. Points of attention
  94. Ice cream cone Testing is Good. Pyramids are Bad. Ice

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

    are the Worst - Stephen H Fishman
  96. Crafting code Testing is Good. Pyramids are Bad. Ice Cream

    Cones are the Worst - Stephen H Fishman
  97. 6. Wrapping up We are almost done! Crafting code

  98. • The liar • Excessive setup • The giant •

    The slow poke • and many more! What we covered
  99. Most of the anti-patterns presented are related to test last

    Test last
  100. If it's hard to test, take a step back. Hard

    to test
  101. Working skeleton Growing object oriented software, guided by tests Steve

    Freeman and Nat Pryce Crafting code
  102. https://www.codurance.com/publications/building-testing-culture

  103. Matheus Marabesi Hello there, you can call me Marabesi, But

    my name is Matheus Marabesi, I work at Codurance as a Software craftsperson. I enjoy talking about anything related to: testing, patterns and gamification. You can find me at @MatheusMarabesi or https://marabesi.com Codurance Crafting Code