Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

1. Recap Episode 1, testing assertions Getting started

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

The Liar The Giant The Mockery 1 The Inspector 7 Generous Leftovers 5 The Local Hero 7 The Nitpicker The Secret Catcher The Dodger The Loudmouth Anti patterns The Greedy Catcher Excessive Setup The Sequencer Hidden Dependency The Enumerator The Stranger The Operating System Evangelist Success Against All Odds The Free Ride The One The Peeping Tom The Slow Poke James Carr - TDD Anti-Patterns

Slide 12

Slide 12 text

Survey

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

6. Wrapping up We are almost done! Crafting code

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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