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

TDD anti patterns - episode 4 - with Javier Mar...

TDD anti patterns - episode 4 - with Javier Martínez

Have you ever seen those anti patterns in your code base?

The greedy catcher, The sequencer, Hidden dependency and The enumerator.

More than 45% of developers haven't, join us to find out more.

Marabesi

March 17, 2022
Tweet

More Decks by Marabesi

Other Decks in Programming

Transcript

  1. TDD - EP 4 codurance.com Testing anti-patterns - The greedy

    catcher, The sequencer, Hidden dependency and The enumerator
  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 greedy catcher 3. The

    sequencer 4. Hidden dependency 5. The enumerator 6. Wrapping up Crafting code Agenda
  4. 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
  5. 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
  6. 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
  7. 2. Anti-patterns - Episode 4 The greedy catcher, The sequencer,

    Hidden dependency and The enumerator Getting started
  8. 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
  9. The Liar The Giant The Mockery The Inspector Generous Leftovers

    The Local Hero The Nitpicker The Secret Catcher The Dodger The Loudmouth Anti patterns The Greedy Catcher 8 Excessive Setup The Sequencer 8 Hidden Dependency 2 The Enumerator 9 The Stranger The Operating System Evangelist Success Against All Odds The Free Ride The One The Peeping Tom The Slow Poke James Carr - TDD Anti-Patterns
  10. describe('Request.headers', function () { it('should work', async () => {

    const { page, server, isChrome } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); if (isChrome) expect(response.request().headers()['user-agent']).toContain('Chrome'); else expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); describe('Request.headers', function () { itChromeOnly('should define Chrome as user agent header', async () => { const { page, server } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); expect(response.request().headers()['user-agent']).toContain('Chrome'); }); itFirefoxOnly('should define Firefox as user agent header', async () => { const { page, server } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); Puppeteer A
  11. describe('Request.headers', function () { it('should work', async () => {

    const { page, server, isChrome } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); if (isChrome) expect(response.request().headers()['user-agent']).toContain('Chrome'); else expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); describe('Request.headers', function () { itChromeOnly('should define Chrome as user agent header', async () => { const { page, server } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); expect(response.request().headers()['user-agent']).toContain('Chrome'); }); itFirefoxOnly('should define Firefox as user agent header', async () => { const { page, server } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); Puppeteer B
  12. describe('Request.headers', function () { it('should work', async () => {

    const { page, server, isChrome } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); if (isChrome) expect(response.request().headers()['user-agent']).toContain('Chrome'); else expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); describe('Request.headers', function () { itChromeOnly('should define Chrome as user agent header', async () => { const { page, server } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); expect(response.request().headers()['user-agent']).toContain('Chrome'); }); itFirefoxOnly('should define Firefox as user agent header', async () => { const { page, server } = getTestState(); const response = await page.goto(server.EMPTY_PAGE); expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); Puppeteer A B
  13. • Could you spot something that seems to be a

    smell? • Were there anti patterns? Anti-patterns?
  14. 2. The greedy catcher - 🏆 8 A unit test

    which catches exceptions and swallows the stack trace, sometimes replacing it with a less informative failure message, but sometimes even just logging (c.f. Loudmouth) and letting the test pass. Crafting code
  15. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  16. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  17. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  18. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  19. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  20. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  21. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  22. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  23. public function test_retrieve_the_latest_payment_for_a_subscription() { $user = $this->createCustomer('retrieve_the_latest_payment_for_a_subscription'); try { $user->newSubscription('main',

    static::$priceId)->create('pm_card_threeDSecure2Required'); $this->fail('Expected exception '.IncompletePayment::class.' was not thrown.'); } catch (IncompletePayment $e) { $subscription = $user->refresh()->subscription('main'); $this->assertInstanceOf(Payment::class, $payment = $subscription->latestPayment()); $this->assertTrue($payment->requiresAction()); } } PHP - Laravel/Cashier stripe
  24. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  25. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  26. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  27. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  28. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  29. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  30. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  31. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  32. try { const token = jwt_decode(req?.cookies['token']); if (token) { return

    null; } else { return await logout($auth, redirect); } } catch (e) { return await logout($auth, redirect); } Jest
  33. it('should logout when token is invalid', async () => {

    const redirect = jest.fn(); const serverParameters: Partial<IContextCookie> = { route: currentRoute as Route, $auth, redirect }; await actions.nuxtServerInit( actionContext as ActionContext, serverParameters as IContextCookie ); expect($auth.logout).toHaveBeenCalled(); }); Jest
  34. it('should logout when token is invalid', async () => {

    const redirect = jest.fn(); const serverParameters: Partial<IContextCookie> = { route: currentRoute as Route, $auth, redirect }; await actions.nuxtServerInit( actionContext as ActionContext, serverParameters as IContextCookie ); expect($auth.logout).toHaveBeenCalled(); }); Jest
  35. it('should logout when token is invalid', async () => {

    const redirect = jest.fn(); const serverParameters: Partial<IContextCookie> = { route: currentRoute as Route, $auth, redirect }; await actions.nuxtServerInit( actionContext as ActionContext, serverParameters as IContextCookie ); expect($auth.logout).toHaveBeenCalled(); }); Jest
  36. it('should logout when token is invalid', async () => {

    const redirect = jest.fn(); const serverParameters: Partial<IContextCookie> = { route: currentRoute as Route, $auth, redirect }; await actions.nuxtServerInit( actionContext as ActionContext, serverParameters as IContextCookie ); expect($auth.logout).toHaveBeenCalled(); }); Jest
  37. it('should logout when token is invalid', async () => {

    const redirect = jest.fn(); const serverParameters: Partial<IContextCookie> = { route: currentRoute as Route, $auth, redirect }; await actions.nuxtServerInit( actionContext as ActionContext, serverParameters as IContextCookie ); expect($auth.logout).toHaveBeenCalled(); }); Jest
  38. 3. The sequencer - 🏆8 A unit test that depends

    on items in an unordered list appearing in the same order during assertions. Crafting code
  39. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  40. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  41. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  42. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  43. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  44. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  45. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  46. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  47. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  48. test(contains three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('contains three fruits', () => { const expectedFruits = ['mango', 'watermelon', 'banana'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Jest - arrays / primitives
  49. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert list(result.columns) == ["id", "quantity", "country", "form", "order_id"] Python
  50. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert list(result.columns) == ["id", "quantity", "country", "form", "order_id"] Python
  51. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert list(result.columns) == ["id", "quantity", "country", "form", "order_id"] Python
  52. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert list(result.columns) == ["id", "quantity", "country", "form", "order_id"] Python
  53. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert list(result.columns) == ["id", "quantity", "country", "form", "order_id"] Python
  54. tests/test_predictions.py::TestPredictions::test_predictions_returns_a_dataframe_with_automatic_pr edictions FAILED [100%] tests/test_predictions.py:16 (TestPredictions.test_predictions_returns_a_dataframe_with_automatic_predictions) ['id', 'quantity', 'form', 'country',

    'order_id'] != ['id', 'quantity', 'country', 'form', 'order_id'] Expected :['id', 'quantity', 'country', 'form', 'order_id'] Actual :['id', 'quantity', 'form', 'country', 'order_id'] Python
  55. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert list(result.columns) == ["id", "quantity", "country", "form", "order_id"] Python
  56. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert ("id" in result.columns and "quantity" in result.columns and "country" in result.columns and "form" in result.columns and "order_id" in result.columns) Python
  57. def test_predictions_returns_a_dataframe_with_automatic_predictions(self,form): # Given order_id = "51a64e87-a768-41ed-b6a5-bf0633435e20" order_info = pd.DataFrame({"order_id":

    [order_id], "form": [form],}) file_path = Path("tests/data/prediction_data.csv") service = FileRepository(file_path) # When result = get_predictions(main_service=service, order_info=order_info) # Then assert set(result.columns) == {"id", "quantity", "country", "form", "order_id"} Python
  58. Points of attention • Know your data structures • Think

    about the role the order plays in a collection
  59. 4. Hidden dependency - 🏆2 A unit test that requires

    some existing data to have been populated somewhere before the test runs. If that data wasn’t populated, the test will fail and leave little indication to the developer what it wanted, or why… forcing them to dig through acres of code to find out where the data it was using was supposed to come from. Crafting code
  60. query: str = """ select product.id po.order_id, po.quantity, product.country from

    product join purchased as pur on pur.product_id = product.id join purchased_order as po on po.purchase_id = cur.id where product.completed is true and pur.type = 'MANUAL' and product.is_test is true ; """ Python
  61. def test_dbdatasource_is_able_to_load_products_related_only_to_manual_purchase( self, db_resource ): # Given config_file_path = Path("./tests/data/configs/docker_config.json")

    expected_result = pd.read_csv("./tests/data/manual_product_info.csv") datasource = DBDataSource(config_file_path=config_file_path) # When result = datasource.get_manual_purchases() # Then assert result.equals(expected_result) Python
  62. def test_dbdatasource_is_able_to_load_products_related_only_to_manual_purchase( self, db_resource ): # Given config_file_path = Path("./tests/data/configs/docker_config.json")

    expected_result = pd.read_csv("./tests/data/manual_product_info.csv") datasource = DBDataSource(config_file_path=config_file_path) # When result = datasource.get_manual_purchases() # Then assert result.equals(expected_result) Python
  63. def test_dbdatasource_is_able_to_load_products_related_only_to_manual_purchase( self, db_resource ): # Given config_file_path = Path("./tests/data/configs/docker_config.json")

    expected_result = pd.read_csv("./tests/data/manual_product_info.csv") datasource = DBDataSource(config_file_path=config_file_path) # When result = datasource.get_manual_purchases() # Then assert result.equals(expected_result) Python
  64. def test_dbdatasource_is_able_to_load_products_related_only_to_manual_purchase( self, db_resource ): # Given config_file_path = Path("./tests/data/configs/docker_config.json")

    expected_result = pd.read_csv("./tests/data/manual_product_info.csv") datasource = DBDataSource(config_file_path=config_file_path) # When result = datasource.get_manual_purchases() # Then assert result.equals(expected_result) Python
  65. def test_dbdatasource_is_able_to_load_products_related_only_to_manual_purchase( self, db_resource ): # Given config_file_path = Path("./tests/data/configs/docker_config.json")

    expected_result = pd.read_csv("./tests/data/manual_product_info.csv") datasource = DBDataSource(config_file_path=config_file_path) # When result = datasource.get_manual_purchases() # Then assert result.equals(expected_result) Python
  66. E assert False E + where False = <bound method

    NDFrame.equals of product_id ... country\n0 851799e1-9b28-4210-5c44-30ddf031ad20 ... 652400 181\n\n[1 rows x 5 columns]>( product_id ... country\n0 2893abc0-eab0-7223-a73d-e39060a7eabe ... 652400 099\n1 4760ff15-52af-1638-0c61-0ebecefc3eb0 ... 652400 130\n2 328f6852-9bf1-e1ce-cf44-8f680537adc6 ... 652400 148\n3 851799e1-9b28-4210-5c44-30ddf031ad20 ... 652400 181\n4 00ab74ed-bd99-63af-ca0c-4f10c8c090db ... 652400 249\n5 2893abc0-eab0-7223-a73d-e39060a7eabe ... 652400 099\n6 4760ff15-52af-1638-0c61-0ebecefc3eb0 ... 652400 130\n7 328f6852-9bf1-e1ce-cf44-8f680537adc6 ... 652400 148\n8 851799e1-9b28-4210-5c44-30ddf031ad20 ... 652400 181\n9 00ab74ed-bd99-63af-ca0c-4f10c8c090db ... 652400 249\n\n[10 rows x 5 columns]) E + where <bound method NDFrame.equals of product_id ... country\n0 851799e1-9b28-4210-5c44-30ddf031ad20 ... 652400 181\n\n[1 rows x 5 columns]> = fragrance_id ... country\n0 851799e1-9b28-4210-5c44-30ddf031ad20 ... 652400 181\n\n[1 rows x 5 columns].equals Python
  67. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  68. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  69. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  70. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  71. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  72. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  73. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store(); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  74. export const Store = () => ({ modules: { user:

    { namespaced: true, state: { currentAdmin: { email: '[email protected]', }, }, getters: userGetters, }, admin: adminStore(adminList).modules.admin, }, }); Jest
  75. export const Store = () => ({ modules: { user:

    { namespaced: true, state: { currentAdmin: { email: '[email protected]', }, }, getters: userGetters, }, admin: adminStore(adminList).modules.admin, }, }); Jest
  76. it('should list admins in the administrator field to be able

    to pick one up', async () => { const store = Store({ admin: { name: 'Admin' } }); const { findByTestId, getByText } = render(AdminPage as any, { store, mocks: { $route: { query: {}, }, }, }); await fireEvent.click(await findByTestId('admin-list')); await waitFor(() => { expect(getByText('Admin')).toBeInTheDocument(); }); }); Jest
  77. • Use some libraries that generates fake data • Test

    data adapters as soon as possible • If possible try to avoid data from external sources Points of attention
  78. 5. The enumerator - 🏆9 A unit test with each

    test case method name is only an enumeration, i.e. test1, test2, test3. As a result, the intention of the test case is unclear, and the only way to be sure is to read the test case code and pray for clarity. Crafting code
  79. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  80. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  81. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  82. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  83. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  84. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  85. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  86. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  87. from status_processor import StatusProcessor def test_set_status(): row_with_status_inactive_1 = dict( row_with__status_inactive_2

    = dict( row_with_status_inactive_3 = dict( row_with_status_inactive_3b = dict( row_with_status_inactive_4 = dict( row_with_status_inactive_5 = dict( The enumerator - Python
  88. • Are we using 1, 2, 3? • The test

    that failed was easy to understand why? Points of attention
  89. • The Greedy Catcher • The Sequencer • Hidden Dependency

    • The Enumerator • and many more! What we covered
  90. 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