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

TDD anti patterns - episode 5 - with Sofia Carballo and Juan Pablo

TDD anti patterns - episode 5 - with Sofia Carballo and 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 stranger, The operating system evangelist, Success against all odds and The free ride.

Ed39ca0d44a6e6cdefc76ac548de5f41?s=128

Marabesi

April 21, 2022
Tweet

More Decks by Marabesi

Other Decks in Programming

Transcript

  1. TDD - EP 5 codurance.com Testing anti-patterns - The stranger,

    The operating system evangelist, Success against all odds and The free ride
  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 stranger 3. The operating

    system evangelist 4. Success against all odds 5. The free ride 6. Wrapping up Crafting code Agenda
  4. 1. Recap Episode 1, Episode 2, Episode 3, Episode 4

    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 8 The Secret Catcher 7 The Dodger 8 The Loudmouth 8 Anti patterns The Greedy Catcher 7 Excessive Setup 3 The Sequencer 7 Hidden Dependency 2 The Enumerator 8 The Stranger The Operating System Evangelist Success Against All Odds The Free Ride The One The Peeping Tom The Slow Poke 6
  7. 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 5. Examples are from open source projects and also extraction from real code bases
  8. MOST POPULAR

  9. None
  10. None
  11. None
  12. None
  13. 2. Anti-patterns - Episode 5 The stranger, The operating system

    evangelist, Success against all odds and The free ride Getting started
  14. 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
  15. 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 7 The Operating System Evangelist 8 Success Against All Odds 3 The Free Ride 8 The One The Peeping Tom The Slow Poke James Carr - TDD Anti-Patterns
  16. 2. The stranger - 🏆 7 Crafting code

  17. 2. The stranger - 🏆 7 A test case that

    doesn’t even belong in the unit test it is part of. it’s really testing a separate object, most likely an object that is used by the object under test, but the test case has gone and tested that object directly without relying on the output from the object under test making use of that object for its own behavior. Crafting code
  18. Usually the strange is not something easy to spot

  19. • It depends on context The stranger

  20. • It depends on context • It is related to

    the xUnit pattern in the section "Test smells" The stranger
  21. • It depends on context • It is related to

    the xUnit pattern in the section "Test smells" • It can be related to mocks The stranger
  22. 3. The Operating System Evangelist - 🏆8 Crafting code

  23. 3. The Operating System Evangelist - 🏆8 A unit test

    that relies on a specific operation system environment to be in place in order to work. Crafting code
  24. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  25. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  26. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  27. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  28. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  29. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  30. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  31. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  32. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  33. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  34. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  35. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  36. class LutrisWrapperTestCase(unittest.TestCase): def test_excluded_initial_process(self): "Test that an excluded process that

    starts a monitored process works" env = os.environ.copy() env['PYTHONPATH'] = ':'.join(sys.path) # run the lutris-wrapper with a bash subshell. bash is "excluded" wrapper_proc = subprocess.Popen( [ sys.executable, lutris_wrapper_bin, 'title', '0', '1', 'bash', 'bash', '-c', "echo Hello World; exec 1>&-; while sleep infinity; do true; done" ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) Python - Lutris
  37. Go lang - Windows portability

  38. Go lang - Windows portability

  39. @Test public void testWithoutSOptionAndWithoutJENKINS_URL() throws Exception { Assume.assumeThat(System.getenv("JENKINS_URL"), is(nullValue())); //

    TODO instead remove it from the process env? assertNotEquals(0, launch("java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "who-am-i") ); } Java - Jenkins
  40. @Test public void testWithoutSOptionAndWithoutJENKINS_URL() throws Exception { Assume.assumeThat(System.getenv("JENKINS_URL"), is(nullValue())); //

    TODO instead remove it from the process env? assertNotEquals(0, launch("java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "who-am-i") ); } Java - Jenkins
  41. FLASH BACK Episode 2 - The local hero Episode 2

    - The local hero
  42. None
  43. https://www.codurance.com/publications/tdd-anti-patterns-chapter-2

  44. 4. Success Against All Odds - 🏆3 Crafting code

  45. 4. Success Against All Odds - 🏆3 A test that

    was written pass first rather than fail first. As an unfortunate side effect, the test case happens to always pass even though the test should fail. Crafting code
  46. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  47. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  48. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  49. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  50. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  51. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  52. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  53. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  54. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  55. @Repository class ProductsRepositoryWithPostgres( private val entityManager: EntityManager ) : Repository

    { override fun listFilteredProducts(query: String?, input: PagingQueryInput?) { val pageRequest: PageRequest = input.asPageRequest() val page: Page<Product> = if (query.isNullOrBlank()) { entityManager.findAll(pageRequest) } else { entityManager.findAllByName(query, pageRequest) } return page } } mockk - kotlin
  56. private fun setupBeforeAll() { productIds = (1..100).map { db().productWithDependencies().apply().get<ProductId>() }

    productIdsContainingWood.addAll( (1..3).map { insertProductWithName("WoodyWoodOrange " + faker.funnyName().name()) } ) productIdsContainingWood.addAll( (1..3).map { insertProductWithName( faker.funnyName().name() + " WoodyWoodOrange " + faker.funnyName().name() ) } ) mockk - kotlin
  57. private fun setupBeforeAll() { productIds = (1..100).map { db().productWithDependencies().apply().get<ProductId>() }

    productIdsContainingWood.addAll( (1..3).map { insertProductWithName("WoodyWoodOrange " + faker.funnyName().name()) } ) productIdsContainingWood.addAll( (1..3).map { insertProductWithName( faker.funnyName().name() + " WoodyWoodOrange " + faker.funnyName().name() ) } ) mockk - kotlin
  58. private fun setupBeforeAll() { productIds = (1..100).map { db().productWithDependencies().apply().get<ProductId>() }

    productIdsContainingWood.addAll( (1..3).map { insertProductWithName("WoodyWoodOrange " + faker.funnyName().name()) } ) productIdsContainingWood.addAll( (1..3).map { insertProductWithName( faker.funnyName().name() + " WoodyWoodOrange " + faker.funnyName().name() ) } ) mockk - kotlin
  59. private fun setupBeforeAll() { productIds = (1..100).map { db().productWithDependencies().apply().get<ProductId>() }

    productIdsContainingWood.addAll( (1..3).map { insertProductWithName("WoodyWoodOrange " + faker.funnyName().name()) } ) productIdsContainingWood.addAll( (1..3).map { insertProductWithName( faker.funnyName().name() + " WoodyWoodOrange " + faker.funnyName().name() ) } ) mockk - kotlin
  60. private fun setupBeforeAll() { productIds = (1..100).map { db().productWithDependencies().apply().get<ProductId>() }

    productIdsContainingWood.addAll( (1..3).map { insertProductWithName("WoodyWoodOrange " + faker.funnyName().name()) } ) productIdsContainingWood.addAll( (1..3).map { insertProductWithName( faker.funnyName().name() + " WoodyWoodOrange " + faker.funnyName().name() ) } ) mockk - kotlin
  61. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  62. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  63. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  64. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  65. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  66. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  67. productIdsContainingWood.addAll( (1..2).map { insertProductWithName(faker.funnyName().name() + " WoodyWoodOrange") } ) cancelledProductId

    = db().productWithDependencies("status" to Product.CANCELLED).apply().get<ProductId>() idsOfStartedByWithCustomerDeadline.addAll( listOf( db().productWithDependencies("customer_deadline" to "2022-04-03T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-02T00:00:00.00Z").apply() .get<ProductId>(), db().productWithDependencies("customer_deadline" to "2022-04-01T00:00:00.00Z").apply() .get<ProductId>(), ) ) } mockk - kotlin
  68. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  69. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  70. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  71. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  72. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  73. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  74. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  75. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  76. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  77. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  78. None
  79. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  80. @Test fun `list products sorted by creation at date descending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_DESC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  81. • Sorting • Pagination Points of attention WHAT COULD WE

    DO?
  82. Focus on a single thing SORTING

  83. @Test fun `list products sorted by creation at date ascending`()

    { val pageQueryInput = PagingQueryInput( size = 30, page = 0, sort = listOf(Sort.CREATED_AT_ASC) ) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(4) assertThat(result.totalElements).isEqualTo(112) assertThat(result.content.size).isEqualTo(30) assertThat(result.content).allSatisfy { productIds.subList(0, 29).contains(it.id) } } mockk - kotlin
  84. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  85. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  86. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  87. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  88. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  89. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  90. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  91. @Test fun `list products sorted by ascending creation date`() {

    db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_ASC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") } mockk - kotlin
  92. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  93. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  94. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  95. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  96. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  97. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  98. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  99. @Test fun `list products sorted by creation at date descending`()

    { db().productWithDependencies("created_at" to "2022-04-01T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-02T00:00:00.00Z").apply() db().productWithDependencies("created_at" to "2022-04-03T00:00:00.00Z").apply() val pageQueryInput = PagingQueryInput(sort = listOf(SortOrder.CREATED_AT_DESC)) val result = repository.listFilteredProducts("", pageQueryInput) assertThat(result.content[0].createdAt).isEqualTo("2022-04-03T00:00:00.00Z") assertThat(result.content[1].createdAt).isEqualTo("2022-04-02T00:00:00.00Z") assertThat(result.content[2].createdAt).isEqualTo("2022-04-01T00:00:00.00Z") } mockk - kotlin
  100. Focus on a single thing PAGINATION

  101. @Test fun `should list products`() { insertTenProducts() val page =

    PagingQueryInput(size = 10) val result = repository.listFilteredProductBriefs(null, null, page) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  102. @Test fun `should list products`() { insertTenProducts() val page =

    PagingQueryInput(size = 10) val result = repository.listFilteredProductBriefs(null, null, page) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  103. @Test fun `should list products`() { insertTenProducts() val page =

    PagingQueryInput(size = 10) val result = repository.listFilteredProductBriefs(null, null, page) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  104. @Test fun `should list products`() { insertTenProducts() val page =

    PagingQueryInput(size = 10) val result = repository.listFilteredProductBriefs(null, null, page) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  105. @Test fun `should list products`() { insertTenProducts() val page =

    PagingQueryInput(size = 10) val result = repository.listFilteredProductBriefs(null, null, page) assertThat(result.currentPage).isEqualTo(0) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  106. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  107. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  108. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  109. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  110. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  111. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin
  112. mockk - kotlin @Test fun `should be on the first

    page by default`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.currentPage).isEqualTo(0) }
  113. mockk - kotlin @Test fun `should be on the first

    page by default`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.currentPage).isEqualTo(0) }
  114. mockk - kotlin @Test fun `should be on the first

    page by default`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.currentPage).isEqualTo(0) }
  115. mockk - kotlin @Test fun `should be on the first

    page by default`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.currentPage).isEqualTo(0) }
  116. @Test fun `should have one page when the list is

    ten`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.totalPages).isEqualTo(1) } mockk - kotlin @Test fun `should be on the first page by default`() { insertTenProducts() val page = PagingQueryInput(size = 10) val result = repository.listFilteredProducts( null, null, Page ) assertThat(result.currentPage).isEqualTo(0) }
  117. 5. The Free Ride - 🏆8 Crafting code

  118. 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
  119. 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
  120. 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
  121. 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 Could you spot something that seems to be a smell?
  122. 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 What about anti-patterns?
  123. 5. The Free Ride - 🏆8 Rather than write a

    new test case method to test another feature or functionality, a new assertion rides along in an existing test case. Crafting code
  124. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  125. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  126. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  127. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  128. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  129. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  130. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  131. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  132. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  133. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  134. it('Page.Events.RequestFailed', async () => { const { page, server, isChrome

    } = getTestState(); await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().endsWith('css')) request.abort(); else request.continue(); }); const failedRequests = []; page.on('requestfailed', (request) => failedRequests.push(request)); await page.goto(server.PREFIX + '/one-style.html'); expect(failedRequests.length).toBe(1); expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); Javascript/Typescript - Puppeteer
  135. public class ToolLocationTest { @Rule public JenkinsRule j = new

    JenkinsRule(); @Test public void toolCompatibility() { Maven.MavenInstallation[] maven = j.jenkins.getDescriptorByType(Maven.DescriptorImpl.class).getInstallations(); assertEquals(1, maven.length); assertEquals("bar", maven[0].getHome()); assertEquals("Maven 1", maven[0].getName()); Ant.AntInstallation[] ant = j.jenkins.getDescriptorByType(Ant.DescriptorImpl.class).getInstallations(); assertEquals(1, ant.length); assertEquals("foo", ant[0].getHome()); assertEquals("Ant 1", ant[0].getName()); JDK[] jdk = j.jenkins.getDescriptorByType(JDK.DescriptorImpl.class).getInstallations(); assertEquals(Arrays.asList(jdk), j.jenkins.getJDKs()); assertEquals(2, jdk.length); // JenkinsRule adds a 'default' JDK assertEquals("default", jdk[1].getName()); // make sure it's really that we're seeing assertEquals("FOOBAR", jdk[0].getHome()); Java - Jenkins
  136. public class ToolLocationTest { @Rule public JenkinsRule j = new

    JenkinsRule(); @Test public void toolCompatibility() { Maven.MavenInstallation[] maven = j.jenkins.getDescriptorByType(Maven.DescriptorImpl.class).getInstallations(); assertEquals(1, maven.length); assertEquals("bar", maven[0].getHome()); assertEquals("Maven 1", maven[0].getName()); Ant.AntInstallation[] ant = j.jenkins.getDescriptorByType(Ant.DescriptorImpl.class).getInstallations(); assertEquals(1, ant.length); assertEquals("foo", ant[0].getHome()); assertEquals("Ant 1", ant[0].getName()); JDK[] jdk = j.jenkins.getDescriptorByType(JDK.DescriptorImpl.class).getInstallations(); assertEquals(Arrays.asList(jdk), j.jenkins.getJDKs()); assertEquals(2, jdk.length); // JenkinsRule adds a 'default' JDK assertEquals("default", jdk[1].getName()); // make sure it's really that we're seeing assertEquals("FOOBAR", jdk[0].getHome()); Java - Jenkins
  137. public class ToolLocationTest { @Rule public JenkinsRule j = new

    JenkinsRule(); @Test public void toolCompatibility() { Maven.MavenInstallation[] maven = j.jenkins.getDescriptorByType(Maven.DescriptorImpl.class).getInstallations(); assertEquals(1, maven.length); assertEquals("bar", maven[0].getHome()); assertEquals("Maven 1", maven[0].getName()); Ant.AntInstallation[] ant = j.jenkins.getDescriptorByType(Ant.DescriptorImpl.class).getInstallations(); assertEquals(1, ant.length); assertEquals("foo", ant[0].getHome()); assertEquals("Ant 1", ant[0].getName()); JDK[] jdk = j.jenkins.getDescriptorByType(JDK.DescriptorImpl.class).getInstallations(); assertEquals(Arrays.asList(jdk), j.jenkins.getJDKs()); assertEquals(2, jdk.length); // JenkinsRule adds a 'default' JDK assertEquals("default", jdk[1].getName()); // make sure it's really that we're seeing assertEquals("FOOBAR", jdk[0].getHome()); Java - Jenkins
  138. public class ToolLocationTest { @Rule public JenkinsRule j = new

    JenkinsRule(); @Test public void toolCompatibility() { Maven.MavenInstallation[] maven = j.jenkins.getDescriptorByType(Maven.DescriptorImpl.class).getInstallations(); assertEquals(1, maven.length); assertEquals("bar", maven[0].getHome()); assertEquals("Maven 1", maven[0].getName()); Ant.AntInstallation[] ant = j.jenkins.getDescriptorByType(Ant.DescriptorImpl.class).getInstallations(); assertEquals(1, ant.length); assertEquals("foo", ant[0].getHome()); assertEquals("Ant 1", ant[0].getName()); JDK[] jdk = j.jenkins.getDescriptorByType(JDK.DescriptorImpl.class).getInstallations(); assertEquals(Arrays.asList(jdk), j.jenkins.getJDKs()); assertEquals(2, jdk.length); // JenkinsRule adds a 'default' JDK assertEquals("default", jdk[1].getName()); // make sure it's really that we're seeing assertEquals("FOOBAR", jdk[0].getHome()); Java - Jenkins
  139. public class ToolLocationTest { @Rule public JenkinsRule j = new

    JenkinsRule(); @Test public void toolCompatibility() { Maven.MavenInstallation[] maven = j.jenkins.getDescriptorByType(Maven.DescriptorImpl.class).getInstallations(); assertEquals(1, maven.length); assertEquals("bar", maven[0].getHome()); assertEquals("Maven 1", maven[0].getName()); Ant.AntInstallation[] ant = j.jenkins.getDescriptorByType(Ant.DescriptorImpl.class).getInstallations(); assertEquals(1, ant.length); assertEquals("foo", ant[0].getHome()); assertEquals("Ant 1", ant[0].getName()); JDK[] jdk = j.jenkins.getDescriptorByType(JDK.DescriptorImpl.class).getInstallations(); assertEquals(Arrays.asList(jdk), j.jenkins.getJDKs()); assertEquals(2, jdk.length); // JenkinsRule adds a 'default' JDK assertEquals("default", jdk[1].getName()); // make sure it's really that we're seeing assertEquals("FOOBAR", jdk[0].getHome()); Java - Jenkins
  140. The automation server

  141. public class ToolLocationTest { @Test @LocalData public void shouldBeCompatibleWithMaven() {

    Maven.MavenInstallation[] maven = j.jenkins.getDescriptorByType(Maven.DescriptorImpl.class).getInstallations(); assertEquals(1, maven.length); assertEquals("bar", maven[0].getHome()); assertEquals("Maven 1", maven[0].getName()); } @Test @LocalData public void shouldBeCompatibleWithAnt() { Ant.AntInstallation[] ant = j.jenkins.getDescriptorByType(Ant.DescriptorImpl.class).getInstallations(); assertEquals(1, ant.length); assertEquals("foo", ant[0].getHome()); assertEquals("Ant 1", ant[0].getName()); } @Test @LocalData Java - Jenkins
  142. @Test @LocalData public void shouldBeCompatibleWithJdk() { JDK[] jdk = j.jenkins.getDescriptorByType(JDK.DescriptorImpl.class).getInstallations();

    assertEquals(Arrays.asList(jdk), j.jenkins.getJDKs()); assertEquals(2, jdk.length); // JenkinsRule adds a 'default' JDK assertEquals("default", jdk[1].getName()); // make sure it's really that we're seeing assertEquals("FOOBAR", jdk[0].getHome()); assertEquals("FOOBAR", jdk[0].getJavaHome()); assertEquals("1.6", jdk[0].getName()); } } Java - Jenkins
  143. 6. Wrapping up We are almost done! Crafting code

  144. • The stranger • The operating system evangelist • Success

    against all odds • The free ride • and two more left! What we covered
  145. https://www.codurance.com/publications/building-testing-culture https://www.codurance.com/publications/tdd-anti-patterns-chapter-1

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

  147. https://www.codurance.com/events

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