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

TDD anti patterns - episode 3 - With Juan Pablo

Ed39ca0d44a6e6cdefc76ac548de5f41?s=47 Marabesi
February 10, 2022

TDD anti patterns - episode 3 - With Juan Pablo

Testing practices has increasing its adoption by developers, shifting left the test responsibilities
And increasing the quality of the code, besides that, continuous testing is an agile practice that impacts the software development life cycle.

In this talk we are going to focus on the following TDD anti-patterns: The nitpicker, The secret catcher, The dodger and The Loudmouth. This is the third out of 6 in the testing anti-patterns series.

Ed39ca0d44a6e6cdefc76ac548de5f41?s=128

Marabesi

February 10, 2022
Tweet

More Decks by Marabesi

Other Decks in Programming

Transcript

  1. TDD - EP 3 codurance.com Testing anti-patterns - The nitpicker,

    The secret catcher, The dodger, The Loudmouth
  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 - Recap 2. The nitpicker 3. The secret

    catcher 4. The dodger 5. The Loudmouth 6. Wrapping up Crafting code Agenda
  4. 1. Recap Episode 1, Episode 2 Getting started

  5. 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
  6. The Liar 4 The Giant 5 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 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
  7. The Liar 4 The Giant 5 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 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
  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. Survey notes: Companies argue that TDD requires more time to complete a task and the team don't have time for it
  9. 2. Anti-patterns - Episode 3 The nitpicker, The secret catcher,

    The dodger and The Loudmounth Getting started
  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
  11. The Liar The Giant The Mockery The Inspector Generous Leftovers

    The Local Hero The Nitpicker8 The Secret Catcher7 The Dodger8 The Loudmouth8 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
  12. Survey

  13. Survey 54.6% Agree

  14. Survey 45.4% Neutral + disagree

  15. 2. The nitpicker - 🏆8 A unit test which compares

    a complete output when it’s really only interested in small parts of it, so the test has to continually be kept in line with otherwise unimportant details. Endemic in web application testing. Crafting code
  16. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  17. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  18. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  19. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  20. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  21. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  22. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertExactJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  23. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  24. public function testDeleteApplication() { $response = $this->postApplication(); $this->assertFalse($response->error); $this->delete('api/application/' .

    $response->data) ->assertJson([ 'data' => (string) $response->data, 'error' => false ]); } PHP - Laravel
  25. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  26. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  27. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  28. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  29. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  30. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  31. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  32. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  33. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  34. describe('#getSignedCookies()', function() { it('should create cookies object', function(done) { var

    result = CloudfrontUtil.getSignedCookies( 'http://foo.com', defaultParams); expect(result).to.have.property('CloudFront-Policy'); expect(result).to.have.property('CloudFront-Signature'); expect(result).to.have.property('CloudFront-Key-Pair-Id'); done(); }); }); Javascript - chai
  35. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  36. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  37. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  38. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  39. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  40. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  41. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  42. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  43. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  44. @Test fun `should calculate CFR correctly by monthly and the

    time split works well ( cross a calendar month)`() { val requestBody = """ { skipped code } """.trimIndent() RestAssured .given() .contentType(ContentType.JSON) .body(requestBody) .post("/api/pipeline/metrics") .then() .statusCode(200) .body("changeFailureRate.summary.value", equalTo(30.0F)) .body("changeFailureRate.summary.level", equalTo("MEDIUM")) .body("changeFailureRate.details[0].value", equalTo("NaN")) .body("changeFailureRate.details[1].value", equalTo("NaN")) .body("changeFailureRate.details[2].value", equalTo(30.0F)) } Kotlin - RestAssured
  45. • Use what you need • It can be generalized

    to other applications (CLI for example) Points of attention
  46. 3. The Secret Catcher - 🏆7 A test that at

    first glance appears to be doing no testing due to the absence of assertions, but as they say, “the devil’s in the details.” The test is really relying on an exception to be thrown when a mishap occurs, and is expecting the testing framework to capture the exception and report it to the user as a failure. Crafting code
  47. Reflection - Java

  48. Reflection - Java

  49. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText(Remove'); await fireEvent.click(removeButton); }); Jest - Javascript
  50. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); }); Jest - Javascript
  51. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); }); Jest - Javascript
  52. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); }); Jest - Javascript
  53. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); }); Jest - Javascript
  54. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); }); Jest - Javascript
  55. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); }); Jest - Javascript 👀
  56. test('it handles error when removing credit card ', async ()

    => { const data = await Payment.asyncData(asyncDataContext); data.paymentMethod = PaymentMethod.CREDIT_CARD; const { getAllByText } = render(Payment, { mocks, data() { return { ...data }; }, }); const [removeButton] = getAllByText('Remove'); await fireEvent.click(removeButton); expect(getByText('Some error')).toBeInTheDocument(); }); Jest - Javascript
  57. Reflection - Java

  58. Software engineering unlocked, Dr. McKayla Because well, if, if you

    know, a smoke test could also execute a lot of the code base. Actually just, you know, make sure that the code run somehow without verifying that, you know, the inputs and outputs are matching and so on. Dr. Mauricio’s Aniche
  59. • Context matters Points of attention

  60. • Lack of practice on TDD • Oriented to coverage

    Root cause
  61. 4. The Dodger - 🏆8 A unit test which has

    lots of tests for minor (and presumably easy to test) side effects, but never tests the core desired behavior. Sometimes you may find this in database access related tests, where a method is called, then the test selects from the database and runs assertions against the result. Crafting code
  62. http://blog.cleancoder.com/uncle-bob/2017/10/03/TestContravariance.html

  63. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  64. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  65. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  66. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  67. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  68. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  69. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  70. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  71. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  72. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  73. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  74. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  75. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  76. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  77. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  78. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  79. /** * Tests that objects works as expected. */ public

    function testObject(): void { $author = Author::createFromArray($this->getSampleId(), $this->getSampleValues()); $this->assertEquals($this->getSampleId(), $author->getId()); $this->assertEquals($this->getSampleValues()['name']['given'], $author->getNameGiven()); $this->assertEquals($this->getSampleValues()['name']['family'], $author->getNameFamily()); $this->assertEquals($this->getSampleValues()['country'], $author->getCountry()); $this->assertEquals($this->getSampleValues()['org']['name'], $author->getOrgName()); $this->assertEquals($this->getSampleValues()['org']['unit'], $author->getOrgUnit()); $this->assertEquals($this->getSampleValues()['homepage'], $author->getHomepage()); $this->assertEquals($this->getSampleValues()['description'], $author->getDescription()); $this->assertEquals($this->getSampleValues()['image'], $author->getImage()); $this->assertEquals($this->getSampleValues()['identification'], $author->getIdentification()); $this->assertEquals($this->getSampleValues()['identification']['email'], $author->getIdentification('email')); $this->assertEquals([], $author->getIdentification('not exist')); $this->assertEquals($author->checksum(), $author->checksum()); } Drupal - PHP
  80. • 1-1 one test class to one production class •

    Focus on implementation details rather than behavior Points of attention
  81. 5. The Loudmouth - 🏆8 A unit test (or test

    suite) that clutters up the console with diagnostic messages, logging messages, and other miscellaneous chatter, even when tests are passing. Crafting code
  82. None
  83. None
  84. None
  85. None
  86. test.each([['function']])( 'should not bubble up the error when a invalid

    source code is provided', (code) => { const strategy = jest.fn(); const result = Reason(code, strategy); expect(strategy).toHaveBeenCalledTimes(0); expect(result).toBeFalsy(); } ); Console log - Javascript
  87. test.each([['function']])( 'should not bubble up the error when a invalid

    source code is provided', (code) => { const strategy = jest.fn(); const result = Reason(code, strategy); expect(strategy).toHaveBeenCalledTimes(0); expect(result).toBeFalsy(); } ); Console log - Javascript
  88. test.each([['function']])( 'should not bubble up the error when a invalid

    source code is provided', (code) => { const strategy = jest.fn(); const result = Reason(code, strategy); expect(strategy).toHaveBeenCalledTimes(0); expect(result).toBeFalsy(); } ); Console log - Javascript
  89. test.each([['function']])( 'should not bubble up the error when a invalid

    source code is provided', (code) => { const strategy = jest.fn(); const result = Reason(code, strategy); expect(strategy).toHaveBeenCalledTimes(0); expect(result).toBeFalsy(); } ); Console log - Javascript
  90. const reason = function(code, strategy) { try { const ast

    = esprima.parseScript(code); if (ast.body.length > 0) { return strategy(ast); } } catch (error) { /* eslint-disable-next-line */ console.warn(error); return false; } }; Console log - Javascript
  91. const reason = function(code, strategy) { try { const ast

    = esprima.parseScript(code); if (ast.body.length > 0) { return strategy(ast); } } catch (error) { /* eslint-disable-next-line */ console.warn(error); return false; } }; Console log - Javascript
  92. const reason = function(code, strategy) { try { const ast

    = esprima.parseScript(code); if (ast.body.length > 0) { return strategy(ast); } } catch (error) { /* eslint-disable-next-line */ console.warn(error); return false; } }; Console log - Javascript
  93. const reason = function(code, strategy) { try { const ast

    = esprima.parseScript(code); if (ast.body.length > 0) { return strategy(ast); } } catch (error) { /* eslint-disable-next-line */ console.warn(error); return false; } }; Console log - Javascript
  94. const originalConsole = globalThis.console; beforeEach(() => { globalThis.console = {

    warn: jest.fn(), error: jest.fn(), log: jest.fn() }; }); afterEach(() => { globalThis.console = originalConsole; }); Console log - Javascript
  95. const originalConsole = globalThis.console; beforeEach(() => { globalThis.console = {

    warn: jest.fn(), error: jest.fn(), log: jest.fn() }; }); afterEach(() => { globalThis.console = originalConsole; }); Console log - Javascript
  96. const originalConsole = globalThis.console; beforeEach(() => { globalThis.console = {

    warn: jest.fn(), error: jest.fn(), log: jest.fn() }; }); afterEach(() => { globalThis.console = originalConsole; }); Console log - Javascript
  97. const originalConsole = globalThis.console; beforeEach(() => { globalThis.console = {

    warn: jest.fn(), error: jest.fn(), log: jest.fn() }; }); afterEach(() => { globalThis.console = originalConsole; }); Console log - Javascript
  98. • Clean up if not needed • Threat the logs

    as a feature, test drive them Points of attention
  99. 6. Wrapping up We are almost done! Crafting code

  100. • The nitpicker • The secret catcher • The dodger

    • The Loudmounth • and many more! What we covered
  101. https://www.codurance.com/publications/tdd-anti-patterns-chapter-1 https://www.codurance.com/publications/tdd-anti-patterns-chapter-2

  102. https://www.codurance.com/publications/building-testing-culture 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