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

TDD anti-patterns at Codurance Spain

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

Marabesi

October 14, 2021
Tweet

More Decks by Marabesi

Other Decks in Programming

Transcript

  1. TDD
    codurance.com
    Testing anti-patterns

    View full-size slide

  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

    View full-size slide

  3. 1. Intro - Background
    2. Excessive setup
    3. The Liar
    4. The giant
    5. The slow poke
    6. Wrapping up
    Crafting code
    Agenda

    View full-size slide

  4. 1. Background
    Warming up
    Getting started

    View full-size slide

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

    View full-size slide

  6. 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

    View full-size slide

  7. 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

    View full-size slide

  8. SSD 14/16: Test Patterns and Anti-Patterns
    Yegor Bugayenko

    View full-size slide

  9. 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

    View full-size slide

  10. 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

    View full-size slide

  11. 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

    View full-size slide

  12. 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

    View full-size slide

  13. 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

    View full-size slide

  14. 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

    View full-size slide

  15. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  18. 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();
    expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true);
    expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true);
    });
    Jest - Javascript - Reactjs

    View full-size slide

  19. 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();
    expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true);
    expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true);
    });
    Jest - Javascript - Reactjs

    View full-size slide

  20. 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();
    expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true);
    expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true);
    });
    Jest - Javascript - Reactjs

    View full-size slide

  21. 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();
    expect(wrapper.instance().state.editorOptions[SOURCE_CODE].readOnly).toBe(true);
    expect(wrapper.find('EditorManager').props().options[SOURCE_CODE].readOnly).toEqual(true);
    });
    Jest - Javascript - Reactjs

    View full-size slide

  22. The automation server

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  29. ● Adding tests after the source code
    ● Lack of SOLID principles
    ● Object Calisthenics?
    Root cause

    View full-size slide

  30. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  34. 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

    View full-size slide

  35. 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

    View full-size slide

  36. 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

    View full-size slide

  37. 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

    View full-size slide

  38. 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

    View full-size slide

  39. @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

    View full-size slide

  40. @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

    View full-size slide

  41. @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

    View full-size slide

  42. ● Wow, async - watch out!
    ● Test without assertions
    Points of attention

    View full-size slide

  43. ● Lack of practice on TDD
    ● Oriented to coverage
    Root cause

    View full-size slide

  44. 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

    View full-size slide

  45. 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

    View full-size slide

  46. 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

    View full-size slide

  47. 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

    View full-size slide

  48. 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

    View full-size slide

  49. 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

    View full-size slide

  50. $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

    View full-size slide

  51. $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

    View full-size slide

  52. $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

    View full-size slide

  53. $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

    View full-size slide

  54. $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

    View full-size slide

  55. $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

    View full-size slide

  56. $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

    View full-size slide

  57. $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

    View full-size slide

  58. $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

    View full-size slide

  59. $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

    View full-size slide

  60. $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

    View full-size slide

  61. $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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  64. ● How many test cases do I have?
    ● Lacks feedback from the test
    ● Data provider / Parameterized tests
    Points of attention

    View full-size slide

  65. ● Test after, instead of test first
    Root cause

    View full-size slide

  66. 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

    View full-size slide

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

    View full-size slide

  68. 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});
    document.dispatchEvent(keypress);
    Jest - Javascript - Reactjs

    View full-size slide

  69. 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});
    document.dispatchEvent(keypress);
    Jest - Javascript - Reactjs

    View full-size slide

  70. 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

    View full-size slide

  71. 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

    View full-size slide

  72. 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

    View full-size slide

  73. 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

    View full-size slide

  74. 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

    View full-size slide

  75. ● CRON jobs
    ● Leap year / any time related
    ● Lack of control over non-determinism
    Points of attention

    View full-size slide

  76. 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

    View full-size slide

  77. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  81. 6. Wrapping up
    We are almost done!
    Crafting code

    View full-size slide

  82. ● The liar
    ● Excessive setup
    ● The giant
    ● The slow poke
    ● and many more!
    What we covered

    View full-size slide

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

    View full-size slide

  84. If it's hard to test, take a step back.
    Hard to test

    View full-size slide

  85. Working skeleton
    Growing object oriented software,
    guided by tests
    Steve Freeman and Nat Pryce
    Crafting code

    View full-size slide

  86. https://www.codurance.com/publications/building-testing-culture

    View full-size slide

  87. 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

    View full-size slide