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

TDD anti patterns - episode 2 - TDD anti patterns - With Giulio Perrone

Marabesi
January 20, 2022

TDD anti patterns - episode 2 - TDD anti patterns - With Giulio Perrone

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, if not testing your code, what will it be then?

In this talk we are going to focus on the following TDD anti-patterns: The mockery, The inspector, Generous leftovers and The local hero. Those are the next set of anti pattern that we are going to cover. If you missed the last session, you can check it on Codurance's platform at https://app.livestorm.co/codurance/testing-anti-patterns-workshop.

Marabesi

January 20, 2022
Tweet

More Decks by Marabesi

Other Decks in Technology

Transcript

  1. TDD - EP 2
    codurance.com
    Testing anti-patterns - The mockery,
    The inspector, The Generous leftovers
    and The local hero

    View 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 Slide

  3. Giulio Perrone
    Hello there!
    As you may have guessed, my name is Giulio and I work at
    Codurance as a Software Craftsperson!
    I enjoy writing katas, discussing algorithms and playing
    video-games.
    Codurance
    Crafting Code

    View Slide

  4. 1. Intro - Recap
    2. The mockery - MM
    3. The inspector - GP
    4. The Generous leftovers - MM
    5. The local hero - GP
    6. Wrapping up
    Crafting code
    Agenda

    View Slide

  5. 1. Recap
    Episode 1, testing assertions
    Getting started

    View 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 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 Slide

  8. Anti patterns - Survey takeaways
    1. Survey notes: Javascript, PHP and Java were the most used programming languages
    2. Survey notes: Practitioners usually informally learn TDD
    3. The anti patterns covered were related to test last
    4. Subjects we touched around testability: SOLID, Object calisthenics, Non-determinism and the
    test pyramid

    View Slide

  9. 2. Anti-patterns - Episode 2
    The mockery, The inspector, Generous
    leftovers and The local hero
    Getting started

    View Slide

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

  11. The Liar
    The Giant
    The Mockery 1
    The Inspector 7
    Generous Leftovers 5
    The Local Hero 7
    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 Slide

  12. Survey

    View Slide

  13. 2. The mockery - 🏆1
    Sometimes mocking can be good, and handy.
    But sometimes developers can lose themselves
    and in their effort to mock out what isn’t being
    tested. In this case, a unit test contains so many
    mocks, stubs, and/or fakes that the system under
    test isn’t even being tested at all, instead data
    returned from mocks is what is being tested.
    Crafting code

    View Slide

  14. class PaymentService(
    private val userRepository: UserRepository,
    private val paymentGateway: PaymentGateway
    ) {
    fun process(
    user: User,
    paymentDetails: PaymentDetails
    ): Boolean {
    if (userRepository.exists(user)) {
    return paymentGateway.pay(paymentDetails)
    }
    return false
    }
    }
    Repository - Kotlin

    View Slide

  15. class PaymentService(
    private val userRepository: UserRepository,
    private val paymentGateway: PaymentGateway
    ) {
    fun process(
    user: User,
    paymentDetails: PaymentDetails
    ): Boolean {
    if (userRepository.exists(user)) {
    return paymentGateway.pay(paymentDetails)
    }
    return false
    }
    }
    Repository - Kotlin

    View Slide

  16. class PaymentService(
    private val userRepository: UserRepository,
    private val paymentGateway: PaymentGateway
    ) {
    fun process(
    user: User,
    paymentDetails: PaymentDetails
    ): Boolean {
    if (userRepository.exists(user)) {
    return paymentGateway.pay(paymentDetails)
    }
    return false
    }
    }
    Repository - Kotlin

    View Slide

  17. class PaymentService(
    private val userRepository: UserRepository,
    private val paymentGateway: PaymentGateway
    ) {
    fun process(
    user: User,
    paymentDetails: PaymentDetails
    ): Boolean {
    if (userRepository.exists(user)) {
    return paymentGateway.pay(paymentDetails)
    }
    return false
    }
    }
    Repository - Kotlin

    View Slide

  18. class TestPaymentService {
    private val userRepository: UserRepository = mockk()
    private val paymentGateway: PaymentGateway = mockk()
    private val paymentService = PaymentService(
    userRepository,
    paymentGateway
    )
    @Test
    fun paymentServiceProcessPaymentFor() {
    val user: User = User()
    every { userRepository.exists(any()) } returns true
    every { paymentGateway.pay(any()) } returns true
    assertTrue(paymentService.process(user, PaymentDetails()))
    }
    }
    Repository - Kotlin

    View Slide

  19. class TestPaymentService {
    private val userRepository: UserRepository = mockk()
    private val paymentGateway: PaymentGateway = mockk()
    private val paymentService = PaymentService(
    userRepository,
    paymentGateway
    )
    @Test
    fun paymentServiceProcessPaymentForUser() {
    val user: User = User()
    every { userRepository.exists(any()) } returns true
    every { paymentGateway.pay(any()) } returns true
    assertTrue(paymentService.process(user, PaymentDetails()))
    }
    }
    Repository - Kotlin

    View Slide

  20. class TestPaymentService {
    private val userRepository: UserRepository = mockk()
    private val paymentGateway: PaymentGateway = mockk()
    private val paymentService = PaymentService(
    userRepository,
    paymentGateway
    )
    @Test
    fun paymentServiceProcessPaymentForUser() {
    val user: User = User()
    every { userRepository.exists(any()) } returns true
    every { paymentGateway.pay(any()) } returns true
    assertTrue(paymentService.process(user, PaymentDetails()))
    }
    }
    Repository - Kotlin

    View Slide

  21. class TestPaymentService {
    private val userRepository: UserRepository = mockk()
    private val paymentGateway: PaymentGateway = mockk()
    private val paymentService = PaymentService(
    userRepository,
    paymentGateway
    )
    @Test
    fun paymentServiceProcessPaymentForUser() {
    val user: User = User()
    every { userRepository.exists(any()) } returns true
    every { paymentGateway.pay(any()) } returns true
    assertTrue(paymentService.process(user, PaymentDetails()))
    }
    }
    Repository - Kotlin

    View Slide

  22. Mocks, stubs, fake, spies
    https://blog.cleancoder.com/uncle-bob/2014/05/14/TheLittleMocker.html

    View Slide

  23. DUMMIES
    You pass in something, and you don’t care who it is used,
    often the object is not used at all.
    STUB
    Opposed to dummies, stubs are objects created in a way
    that you do care how they are used. For example, to tricky
    an authorization to test if the user ca/can’t do certain actions
    in the system.
    SPIES
    To assert that a method was called by the system under test,
    as the post by [1]: “You can use Spies to see inside the
    workings of the algorithms you are testing”.
    True mocks, stubs, dummies, spies, fakes
    TRUE MOCKS
    Is interested in the behavior, instead of return of functions. It
    cares about which functions were invoked, with what
    arguments and how often.
    FAKES
    Fakes have business logic, so it can drive the system under
    test with different sets of data.

    View Slide

  24. DUMMIES
    You pass in something, and you don’t care who it is used,
    often the object is not used at all.
    STUB
    Opposed to dummies, stubs are objects created in a way
    that you do care how they are used. For example, to tricky
    an authorization to test if the user ca/can’t do certain actions
    in the system.
    SPIES
    To assert that a method was called by the system under test,
    as the post by [1]: “You can use Spies to see inside the
    workings of the algorithms you are testing”.
    True mocks, stubs, dummies, spies, fakes
    TRUE MOCKS
    Is interested in the behavior, instead of return of functions. It
    cares about which functions were invoked, with what
    arguments and how often.
    FAKES
    Fakes have business logic, so it can drive the system under
    test with different sets of data.

    View Slide

  25. DUMMIES
    You pass in something, and you don’t care who it is used,
    often the object is not used at all.
    STUB
    Opposed to dummies, stubs are objects created in a way
    that you do care how they are used. For example, to tricky
    an authorization to test if the user can/can’t do certain
    actions in the system.
    SPIES
    To assert that a method was called by the system under test,
    as the post by [1]: “You can use Spies to see inside the
    workings of the algorithms you are testing”.
    True mocks, stubs, dummies, spies, fakes
    TRUE MOCKS
    Is interested in the behavior, instead of return of functions. It
    cares about which functions were invoked, with what
    arguments and how often.
    FAKES
    Fakes have business logic, so it can drive the system under
    test with different sets of data.

    View Slide

  26. DUMMIES
    You pass in something, and you don’t care who it is used,
    often the object is not used at all.
    STUB
    Opposed to dummies, stubs are objects created in a way
    that you do care how they are used. For example, to tricky
    an authorization to test if the user ca/can’t do certain actions
    in the system.
    SPIES
    To assert that a method was called by the system under test,
    as the post by [1]: “You can use Spies to see inside the
    workings of the algorithms you are testing”.
    True mocks, stubs, dummies, spies, fakes
    TRUE MOCKS
    Is interested in the behavior, instead of return of functions. It
    cares about which functions were invoked, with what
    arguments and how often.
    FAKES
    Fakes have business logic, so it can drive the system under
    test with different sets of data.

    View Slide

  27. DUMMIES
    You pass in something, and you don’t care who it is used,
    often the object is not used at all.
    STUB
    Opposed to dummies, stubs are objects created in a way
    that you do care how they are used. For example, to tricky
    an authorization to test if the user ca/can’t do certain actions
    in the system.
    SPIES
    To assert that a method was called by the system under test,
    as the post by [1]: “You can use Spies to see inside the
    workings of the algorithms you are testing”.
    True mocks, stubs, dummies, spies, fakes
    TRUE MOCKS
    Is interested in the behavior, instead of return of functions. It
    cares about which functions were invoked, with what
    arguments and how often.
    FAKES
    Fakes have business logic, so it can drive the system under
    test with different sets of data.

    View Slide

  28. DUMMIES
    You pass in something, and you don’t care who it is used,
    often the object is not used at all.
    STUB
    Opposed to dummies, stubs are objects created in a way
    that you do care how they are used. For example, to tricky
    an authorization to test if the user ca/can’t do certain actions
    in the system.
    SPIES
    To assert that a method was called by the system under test,
    as the post by [1]: “You can use Spies to see inside the
    workings of the algorithms you are testing”.
    True mocks, stubs, dummies, spies, fakes
    TRUE MOCKS
    Is interested in the behavior, instead of return of functions. It
    cares about which functions were invoked, with what
    arguments and how often.
    FAKES
    Fakes have business logic, so it can drive the system under
    test with different sets of data.

    View Slide

  29. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  30. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  31. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  32. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  33. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  34. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  35. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  36. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  37. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  38. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createDummie(FileRepository::class);
    $repository = $this->createDummie(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $assembler->packVersion(new Release());
    }
    Stub - PHP

    View Slide

  39. public function test_should_generate_new_version()
    {
    $file = new File('1', 'myfile.json', 'src/myfile.json', '{}');
    $fileRepository = $this->createStub(FileRepository::class);
    $fileRepository->method('findFile')
    ->willReturn(
    $file
    );
    $branch = 'master';
    $filesToReleaseRepository =
    $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly($this->findVersion, $fileRepository, $branch,
    $filesToReleaseRepository);
    $assembler->setFilesToWriteRelease([$file]);
    Stub - PHP

    View Slide

  40. public function test_should_generate_new_version()
    {
    $file = new File('1', 'myfile.json', 'src/myfile.json', '{}');
    $fileRepository = $this->createStub(FileRepository::class);
    $fileRepository->method('findFile')
    ->willReturn(
    $file
    );
    $branch = 'master';
    $filesToReleaseRepository =
    $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly($this->findVersion, $fileRepository, $branch,
    $filesToReleaseRepository);
    $assembler->setFilesToWriteRelease([$file]);
    Stub - PHP

    View Slide

  41. public function test_should_generate_new_version()
    {
    $file = new File('1', 'myfile.json', 'src/myfile.json', '{}');
    $fileRepository = $this->createStub(FileRepository::class);
    $fileRepository->method('findFile')
    ->willReturn(
    $file
    );
    $branch = 'master';
    $filesToReleaseRepository =
    $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly($this->findVersion, $fileRepository, $branch,
    $filesToReleaseRepository);
    $assembler->setFilesToWriteRelease([$file]);
    Stub - PHP

    View Slide

  42. public function test_should_generate_new_version()
    {
    $file = new File('1', 'myfile.json', 'src/myfile.json', '{}');
    $fileRepository = $this->createStub(FileRepository::class);
    $fileRepository->method('findFile')
    ->willReturn(
    $file
    );
    $branch = 'master';
    $filesToReleaseRepository =
    $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly($this->findVersion, $fileRepository, $branch,
    $filesToReleaseRepository);
    $assembler->setFilesToWriteRelease([$file]);
    Stub - PHP

    View Slide

  43. The library and the anti-pattern are different things
    Mockery != Mockery

    View Slide

  44. ● Historic reasons / TDD styles?
    ● As simple as the code is, the easiest is to overuse it
    ● Also, no experience in TDD
    Root cause

    View Slide

  45. 3. The inspector - 🏆9
    A unit test that violates encapsulation in an
    effort to achieve 100% code coverage, but
    knows so much about what is going on in the
    object that any attempt to refactor will break
    the existing test and require any change to
    be reflected in the unit test.
    Crafting code

    View Slide

  46. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $release = $assembler->packVersion(new Release());
    $this->assertEquals('0.0.12', $release->getVersion());
    }
    Getters and setters - PHP

    View Slide

  47. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $release = $assembler->packVersion(new Release());
    $this->assertEquals('0.0.12', $release->getVersion());
    }
    Getters and setters - PHP

    View Slide

  48. public function test_should_throw_error_if_no_files_are_found_to_release()
    {
    $this->expectException(NoFilesToRelease::class);
    $fileRepository = $this->createStub(FileRepository::class);
    $repository = $this->createStub(FilesToReleaseRepository::class);
    $assembler = new Assembly(
    $this->findVersion, $fileRepository, 'master', $repository
    );
    $assembler->setFilesToWriteRelease([]);
    $release = $assembler->packVersion(new Release());
    $this->assertEquals('0.0.12', $release->getVersion());
    }
    Getters and setters - PHP

    View Slide

  49. View Slide

  50. View Slide

  51. package com.mypackage
    class EmployeeUnitTest {
    private var company: Company = CompanyBuilder.build()
    init {
    FieldUtils.writeField(company, "id", CompanyId.randomId(), true)
    }
    }
    Reflection- Kotlin

    View Slide

  52. package com.mypackage
    class EmployeeUnitTest {
    private var company: Company = CompanyBuilder.build()
    init {
    FieldUtils.writeField(company, "id", CompanyId.randomId(), true)
    }
    }
    Reflection- Kotlin

    View Slide

  53. package com.mypackage
    class EmployeeUnitTest {
    private var company: Company = CompanyBuilder.build()
    init {
    FieldUtils.writeField(company, "id", CompanyId.randomId(), true)
    }
    }
    Reflection- Kotlin

    View Slide

  54. public void testSquare()
    {
    Class classToTest = SomeApplicationClass.class;
    Method privateMethod = classToTest.getMethod(
    "computeSquare", new Class[]{Integer.class}
    );
    String result = (String) privateMethod.invoke(
    new Integer(3)
    );
    assertEquals("9", result);
    }
    Reflection - Java

    View Slide

  55. public void testSquare()
    {
    Class classToTest = SomeApplicationClass.class;
    Method privateMethod = classToTest.getMethod(
    "computeSquare", new Class[]{Integer.class}
    );
    String result = (String) privateMethod.invoke(
    new Integer(3)
    );
    assertEquals("9", result);
    }
    Reflection - Java

    View Slide

  56. public void testSquare()
    {
    Class classToTest = SomeApplicationClass.class;
    Method privateMethod = classToTest.getMethod(
    "computeSquare", new Class[]{Integer.class}
    );
    String result = (String) privateMethod.invoke(
    new Integer(3)
    );
    assertEquals("9", result);
    }
    Reflection - Java

    View Slide

  57. public void testSquare()
    {
    Class classToTest = SomeApplicationClass.class;
    Method privateMethod = classToTest.getMethod(
    "computeSquare", new Class[]{Integer.class}
    );
    String result = (String) privateMethod.invoke(
    new Integer(3)
    );
    assertEquals("9", result);
    }
    Reflection - Java

    View Slide

  58. public void testSquare()
    {
    Class classToTest = SomeApplicationClass.class;
    Method privateMethod = classToTest.getMethod(
    "computeSquare", new Class[]{Integer.class}
    );
    String result = (String) privateMethod.invoke(
    new Integer(3)
    );
    assertEquals("9", result);
    }
    Reflection - Java

    View Slide

  59. ● Reflection - Encapsulation
    ● Did I hear coverage?
    Points of attention

    View Slide

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

    View Slide

  61. 4. Generous Leftovers - 🏆5
    An instance where one unit test creates
    data that is persisted somewhere, and
    another test reuses the data for its own
    devious purposes. If the “generator” is
    ran afterward, or not at all, the test using
    that data will outright fail.
    Crafting code

    View Slide

  62. View Slide

  63. View Slide

  64. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  65. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  66. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  67. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  68. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  69. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript


    View Slide

  70. test('Testing twice', () => {
    mockFn.mockClear();
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  71. test('Testing twice', () => {
    mockFn.mockClear();
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript

    View Slide

  72. const mockFn = jest.fn();
    function fnUnderTest(args1) {
    mockFn(args1);
    }
    test('Testing once', () => {
    fnUnderTest('first-call');
    expect(mockFn).toHaveBeenCalledWith('first-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    test('Testing twice', () => {
    mockFn.mockClear();
    fnUnderTest('second-call');
    expect(mockFn).toHaveBeenCalledWith('second-call');
    expect(mockFn).toHaveBeenCalledTimes(1);
    });
    Jest - Javascript


    View Slide

  73. ● Persistent fixtures can become a source of errors
    ● Your tests are coupled on a specific sequence to be executed
    ● Persistent fixtures can make test slower
    Points of attention

    View Slide

  74. ● Lack of practice on TDD
    ● Mixing different types of tests, integration/unit/end-to-end
    Root cause

    View Slide

  75. 5. The local hero - 🏆7
    A test case that is dependent on
    something specific to the development
    environment it was written on in order
    to run. The result is the test passes on
    development boxes, but fails when
    someone attempts to run it elsewhere.
    Crafting code

    View Slide

  76. View Slide

  77. import { Component } from 'react';
    import Button from '../../buttons/primary/Primary';
    import '../../../../scss/shake-horizontal.scss';
    import './survey.scss';
    const config = {
    surveyUrl: process.env.REACT_APP_SURVEY_URL || '',
    }
    const survey = config.surveyUrl;
    const mapStateToProps = state => ({
    user: state.userReducer.user,
    });
    export class Survey extends Component {
    Javascript - ReactJs

    View Slide

  78. import { Component } from 'react';
    import Button from '../../buttons/primary/Primary';
    import '../../../../scss/shake-horizontal.scss';
    import './survey.scss';
    const config = {
    surveyUrl: process.env.REACT_APP_SURVEY_URL || '',
    }
    const survey = config.surveyUrl;
    const mapStateToProps = state => ({
    user: state.userReducer.user,
    });
    export class Survey extends Component {
    Javascript - ReactJs

    View Slide

  79. import { Component } from 'react';
    import Button from '../../buttons/primary/Primary';
    import '../../../../scss/shake-horizontal.scss';
    import './survey.scss';
    const config = {
    surveyUrl: process.env.REACT_APP_SURVEY_URL || '',
    }
    const survey = config.surveyUrl;
    const mapStateToProps = state => ({
    user: state.userReducer.user,
    });
    export class Survey extends Component {
    Javascript - ReactJs

    View Slide

  80. import { Component } from 'react';
    import Button from '../../buttons/primary/Primary';
    import '../../../../scss/shake-horizontal.scss';
    import './survey.scss';
    const config = {
    surveyUrl: process.env.REACT_APP_SURVEY_URL || '',
    }
    const survey = config.surveyUrl;
    const mapStateToProps = state => ({
    user: state.userReducer.user,
    });
    export class Survey extends Component {
    Javascript - ReactJs

    View Slide

  81. import { Component } from 'react';
    import Button from '../../buttons/primary/Primary';
    import '../../../../scss/shake-horizontal.scss';
    import './survey.scss';
    const config = {
    surveyUrl: process.env.REACT_APP_SURVEY_URL || '',
    }
    const survey = config.surveyUrl;
    const mapStateToProps = state => ({
    user: state.userReducer.user,
    });
    export class Survey extends Component {
    Javascript - ReactJs

    View Slide

  82. test('show up button when loading is done and skip prop is true', () => {
    const user = { uid: 'uhiuqwqw-k-woqk-wq--qw' };
    const wrapper = mount();
    wrapper.setState({
    loading: false
    });
    expect(wrapper.find(Button).length).toBe(1);
    });
    Javascript - ReactJs

    View Slide

  83. test('show up button when loading is done and skip prop is true', () => {
    const user = { uid: 'uhiuqwqw-k-woqk-wq--qw' };
    const wrapper = mount();
    wrapper.setState({
    loading: false
    });
    expect(wrapper.find(Button).length).toBe(1);
    });
    Javascript - ReactJs

    View Slide

  84. test('show up button when loading is done and skip prop is true', () => {
    const user = { uid: 'uhiuqwqw-k-woqk-wq--qw' };
    const wrapper = mount();
    wrapper.setState({
    loading: false
    });
    expect(wrapper.find(Button).length).toBe(1);
    });
    Javascript - ReactJs

    View Slide

  85. test('show up button when loading is done and skip prop is true', () => {
    const user = { uid: 'uhiuqwqw-k-woqk-wq--qw' };
    const wrapper = mount();
    wrapper.setState({
    loading: false
    });
    expect(wrapper.find(Button).length).toBe(1);
    });
    Javascript - ReactJs

    View Slide

  86. test('show up button when loading is done and skip prop is true', () => {
    const user = { uid: 'uhiuqwqw-k-woqk-wq--qw' };
    const wrapper = mount();
    wrapper.setState({
    loading: false
    });
    expect(wrapper.find(Button).length).toBe(1);
    });
    Javascript - ReactJs

    View Slide

  87. test('show up button when loading is done and skip prop is true', () => {
    const user = { uid: 'uhiuqwqw-k-woqk-wq--qw' };
    const wrapper = mount();
    wrapper.setState({
    loading: false
    });
    expect(wrapper.find(Button).length).toBe(1);
    });
    Javascript - ReactJs

    View Slide

  88. View Slide

  89. View Slide

  90. View Slide

  91. ● File system
    ● Dependencies on the operating system
    ● External configuration management
    Points of attention

    View Slide

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

    View Slide

  93. ● The Mockery
    ● The inspector
    ● The generous leftovers
    ● The local hero
    ● and many more!
    What we covered

    View Slide

  94. https://www.codurance.com/publications/building-testing-culture
    https://www.codurance.com/publications/tdd-anti-patterns-chapter-1

    View Slide

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

    View Slide

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

  97. Giulio Perrone
    Hello there!
    As you may have guessed, my name is Giulio and I work at
    Codurance as a Software Craftsperson!
    I enjoy writing katas, discussing algorithms and playing
    video-games.
    Codurance
    Crafting Code

    View Slide