The Little Shop of TDD Horrors

83
@giorgionatili #mobiletea 1 The Little Shop of TDD Horrors a story from Giorgio Natili (the mobile & tdd dude)

Transcript of The Little Shop of TDD Horrors

Page 1: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 1

The Little Shop of TDD Horrors

a story from Giorgio Natili (the mobile & tdd dude)

Page 2: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 2

Italian Accent Crash Course

Page 3: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 3

A few words about me…

Page 4: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 4

Lead Mobile Engineer (McGraw Hill Education)

@giorgionatili

Page 5: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 5

Mobile Tea

Page 6: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 6

www.meetup.com/pro/mobiletea

Page 7: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 7

Setup and Teardown

Tests clarity and readability

Mocking data

Contract tests

Behaviors driven development

End to end testing

The goals of testing

What we’ll talk about

Page 8: The Little Shop of TDD Horrors

“We are going to cycle in good and bad practices highlighting potential solutions…”

How we’ll talk about this

Page 9: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 9

Everything Started with a Red Sign…

Page 10: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 10

And from a bunch of notes

Page 11: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 11

What’s TDD

Page 12: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 12

Test Driven Development

Page 13: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 13

Development life cycleIt’s all about writing enough code to satisfy specific conditions and a continuous refactoring

Page 14: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 14

Analysis toolDecouple requirements in independent, negotiable, valuable, estimable, small testable pieces of code

Page 15: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 15

With TDD you are not lost anymore!

Page 16: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 16

Set up and tear down

Page 17: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 17

Easy setup and teardownUnderstandable setups keep the test clean and easily comprehensible for everybody

Page 18: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 18

beforeEach(function() {

provider.init(["course:read:*", "group:read:*", "settings:read:*", "settings:update:*", "activity:read:*", "log:*:*", "node:create:*", "settings:create:*", "system:update:*", "system:read:*", "system:delete:*", "system:create:*", "unit:create:*", "unit:read:*", "node:update:*", "node:read:*", "node:delete:*", "splash:read:*", "path:read:*", "master:update:*", "snapshot:read:*", "lti:grade:*", "master:read:*", "snapshot:update:*", "history:read:*", "lti:appLaunch:*", "lti:contentItemReturn:*", "appAction:annotationBundle.js:*", "appAction:apps:*", "appAction:annotation.js:*", "openId:read:*", "app:read:*", "activityOutcome:activitySubmission:*", "search:read:*", "userOrgProfile:read:*", "lti:contentItemSelectionReturn:*", "history:create:*", "eula:read:*", "lti:editActivity:*", "userAppCategoryPref:update:*", "platformArtifacts:read:*", "userAppCategoryPref:read:*", "userAppCategoryPref:delete:*", "userAppCategoryPref:create:*", "appCategory:read:*", "appActivity:update:3,4,16,48,49", "appActivity:findAddible:16,48,49", "nextbook:read:*", "appActivity:read:*", "lti:addActivity:*", "activityOutcome:create:*", "activityOutcome:read:*", "activityOutcome:update:*" ]);

});

Overcomplicated setup is a symptom of poor test(s) organization!

Page 19: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 19

Eventually decouple setups

Just in case you really need complicated setups decouple them from tests and include them in the setup…

Page 20: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 20

Essentials Tests

Page 21: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 21

Test the fundamentals

If you are not 100% test covered you are not a bad person!

Page 22: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 22

describe( 'AppCtrl', function() { describe( 'isCurrentUrl', function() { var AppCtrl, $location, $scope;

beforeEach( module( 'ngBoilerplate' ) );

beforeEach( inject( function( $controller, _$location_, $rootScope ) { $location = _$location_; $scope = $rootScope.$new(); AppCtrl = $controller( 'AppCtrl', { $location: $location, $scope: $scope }); }));

it( 'should pass a dummy test', inject( function() { expect( AppCtrl ).toBeTruthy(); })); }); });

Don’t take advantage from this!

Page 23: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 23

Test, design and code quality

Page 24: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 24

Big up front design is not TDD

It’s not based on cycles and it’s not how you would design a component…

Page 25: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 25

Big projects should still follow best practices…

Page 26: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 26

Tests are code, treat them well

Write small, readable and decoupled tests. Tests are part of your code base, don’t write garbage code!

Page 27: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 27

Treat your tests as a welcome guests

Page 28: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 28

Refactor on green

Don’t refactor code or tests until the tests are not green…

Page 29: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 29

Readability

Page 30: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 30

Readable

Never write tests that nobody wants or is able to read, you’ll stop to read them too

Page 31: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 31

@implementation CardTests

- (void)testFlag { [self login]; NSManagedObjectContext *context = [CoreDataManager sharedManager].operationContext; Section *testSection = [Section sectionForUpsertWithSectionId:@(1) forUserId:self.testUserId inContext:context];

// Completed activities should not show a due soon indicator even when due soon. Activity *activityNotStarted = [Activity activityForUpsertWithActivityId:@(2) forSection:testSection inContext:context]; activityNotStarted.progressStatus = ActivityProgressStatusNotYetStarted; activityNotStarted.due = [NSDate distantFuture]; Card *cardForActivityNotStarted = [Card cardForUpsertWithActivityDue:activityNotStarted]; XCTAssertEqualObjects(SCLocStr(@"ACTIVITY_FLAG_NOT_STARTED"), cardForActivityNotStarted.flag);

// Repeated for 6 different scenarios…

@end

Boring and not useful

Page 32: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 32

Do you like to read like this?

Page 33: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 33

Time box writing tests

You should be driven away from writing long tests and large amounts of code to satisfy them

Page 34: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 34

Privacy

Page 35: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 35

Respect the privacy

Don’t expose methods just because you have to test it: Well-designed public methods should use them in such a way they don’t need be directly tested

Page 36: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 36

Reflection + methods swizzling

Don’t expose private methods -even temporarily - you are terrorizing your own design

Page 37: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 37

Repetitive Cases

Page 38: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 38

Automate repetitive cases

Use generative tests framework to mock repetitive cases in your tests

Page 39: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 39

describe('pow', function() { it('works for simple cases', function() { assert.equal(pow(2, 1), 2); assert.equal(pow(2, 2), 4); assert.equal(pow(2, 5), 32); assert.equal(pow(2, 8), 256); assert.equal(pow(3, 2), 9); assert.equal(pow(10, 4), 10000); assert.equal(pow(1, 99), 1); }); });

From this mess…

Page 40: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 40

forAll([gentest.types.int, gentest.types.int],

'pow works like builtin',

function(base, exponent) { var builtinPow = Math.pow(base, exponent); var ourPow = pow(base, exponent);

return (builtinPow === ourPow || relativeErrorWithin(0.000000001, builtinPow, ourPow)); });

To a better code!

Page 41: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 41

You feel better!

Page 42: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 42

Clarity

Page 43: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 43

Clarity

The clarity of expectations and assertions is the key for effective TDD

Page 44: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 44

Naming

Don’t be worried about the length of the names, a right name is the key to be understood by the others

Page 45: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 45

Poor descriptions

Tests descriptions are one of the best sources of documentation, never ignore the importance of providing a good description

Page 46: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 46

Respect laziness

Never force a stakeholder to read the code contained in a test to understand its purpose

Page 47: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 47

Arrange, Act, Assert (AAA)

Separate what is going to be tested from the setups and keep the verification steps clearly identified

Page 48: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 48

Conditional Behaviors

Page 49: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 49

Avoid conditional behaviors

If you start to add conditionals to determine if your code is executed by the tests suites, it means that you are in danger

Page 50: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 50

Ambiguous behaviors are dangerous!

Page 51: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 51

“TDD is not about preventing bugs, it’s about proving the

exactness of behaviors”

Page 52: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 52

There are unpredictable conditions!

Page 53: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 53

but you can still mitigate them…

Page 54: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 54

Network Responses and API

Page 55: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 55

Network Responses

Applications rely on external data sources, it’s important to have clear expectations about external APIs responses

Page 56: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 56

Performance

Tests are code: Never ignore performance issues and bottlenecks

Page 57: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 57

Mock Data

Mock only the data you are testing. A mock should be small and understandable

Page 58: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 58

Contract Tests

Create tests in a separate environment that checks the sanity of the external API you rely on

Page 59: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 59

'use strict'; var supertest = require('supertest-as-promised'); var request = supertest('https://ngcourse.herokuapp.com'); var assert = require('chai').assert;

describe('Backend API', function() { it ('should return the list of users', function() { return request.get('/api/v1/users') .expect(200); }); });

Contract Tests in Practice

Page 60: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 60

Behaviors

Page 61: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 61

Behaviors

Focus on testing software behaviors rather then single units of code

Page 62: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 62

Organize behaviors

Group tests and behaviors that are tied to specific, testable functionality of your software

Page 63: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 63

Connect behaviors

Create a connection between behaviors and scenarios with a format like given/then/when

Page 64: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 64

Divide tests logically

Never violate the Single Responsibility Principle neither when writing tests, keep the ordered and in a good shape is the key to get the more value from them…

Page 65: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 65

Measure complexity

If you are not able to organize your tests in such a way it means that you are dealing with confusing (poor!) requirements

Page 66: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 66

Scenarios -> Use Cases -> Behaviors

This connection is the key to defining tests that clearly describe a feature and its acceptance criteria

Page 67: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 67

End 2 End Testing

Page 68: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 68

Test for pixels

Don’t use e2e tests for testing layout implementations, automate (eventually) screenshots and then examine them

Page 69: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 69

Counting elements

Don’t use e2e testing to count UI elements, use them to describe how the UI *should change* with a specific behavior

Page 70: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 70

Writing E2E tests

Never wait too much, start to write end user tests as soon as the requirements are ready, the lack of automation can bring to not reliable outputs

Page 71: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 71

The Goals of Testing

Page 72: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 72

Do you ever asked to yourself?

Page 73: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 73

Profitable

Tests should always support the end goal of making profit from a software

Page 74: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 74

Maintainability

Tests are a means to increase maintainability of a software

Page 75: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 75

Common understanding

Don’t separate developers and testers! Keeping them together allows you to have testers that understand the app internals

Page 76: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 76

Interchangeability

Having pairing working alternatively on code and tests is the key to having different perspectives and to improve interchangeability in your team

Page 77: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 77

Conclusions

Page 78: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 78

Preconditions

If you cannot define preconditions you cannot write tests of good quality, potentially there is a big misunderstanding about requirements

Page 79: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 79

Quality

The quality of a test can be highly opinionated; let’s find a metric that is valuable for your organization

Page 80: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 80

Validate

Write tests to validate the understanding of the behaviors a software should implement not to validate the code

Page 81: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 81

Estimate

Writing tests take time. Count it in your estimates but keep always in mind the time that is then saved during the maintaining phase

Page 82: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 82

Questions

Page 83: The Little Shop of TDD Horrors

@giorgionatili #mobiletea 83

THANK YOU