testing and object-oriented designdesign happens every day naming variables choosing function...

48
Testing and Object-Oriented Design Matt Dickenson March 16, 2016

Upload: others

Post on 25-Aug-2020

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Testing and Object-Oriented Design

Matt Dickenson March 16, 2016

Page 2: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing
Page 3: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Outline

Overall recommendations

Specific examples

General guidelines

Page 4: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Why Test?

confidence

edge cases

regressions

easier refactoring

working examples

inform design

Page 5: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Model 1: Linear

design

build

test

Page 6: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Model 2: Cyclicalbuild

testdesign

Page 7: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Design Happens Every Day

naming variables

choosing function parameters

determining class boundaries

organizing files

structuring projectsestablishing service interfaces

Page 8: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Questions for Design

🌍 Does this convey knowledge about the world?

🙀 Would this decision surprise another developer?

🗓 Will I remember in 6 months why I did this?

Page 9: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Tests are an essential part of your development toolkit.

(but not exclusively)

especially when working in a dynamically typed, interpreted

language

Page 10: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

public class CollectionTest { public static int add(int num1, int num2){ return num1 * num2; } }

int expected = 4; int result = add(2, 2); assertEquals(result, expected);

Do Static Types Make Tests Unnecessary?

Page 11: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

“All tests passing.”

Page 12: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Types of Testing

✔ Unit

📲 Integration

& Acceptance

📈 Functional

🔥 Failure

Page 13: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Anatomy of a Test

Given

When

Then

Page 14: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

✔ Unit Test

Given input 1 and 2

When sum() is called

Then output should be 3

Page 15: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

📲 Integration Test

Given a client and server

When the client parses the server payload

Then the app state should be updated

Page 16: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

“Two unit tests, no integration tests.”

Page 17: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

🔥 Failure Test

Given a system deployed in SJC and DCA

When SJC goes down

Then the system should remain reachable

Page 18: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

🔥 Failure Test

Page 19: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Do Don’t

Create flaky tests

Leave side effects

Mock object under test

Inject dependencies

Check edge cases

Use mocks & stubs

Page 20: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing
Page 21: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Refactoring for Testability

Page 22: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

x = time.time() random.seed(x) y = random.random() sys.stdout.write(str(y))

Page 23: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

x = time.time() random.seed(x) y = random.random() sys.stdout.write(str(y))

What does this code do?How would you test it?

How would you refactor it?

Page 24: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

x = time.time() random.seed(x) y = random.random() sys.stdout.write(str(y))

What does this code do?How would you test it?

How would you refactor it?

uninformative names

can’t be imported

Page 25: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def main(): now = time.time() random.seed(now) rand = random.random() sys.stdout.write(str(rand))

if __name__ == '__main__': main()

Page 26: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def main(): now = time.time() random.seed(now) rand = random.random() sys.stdout.write(str(rand))

if __name__ == '__main__': main()

better names

can now be imported

Page 27: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def main(args): now = time.time() random.seed(now) rand = random.random() with open(args.output, 'w') as f: f.write(str(rand))

if __name__ == '__main__': parser = ArgumentParser() parser.add_argument('--output', required=True, help="Output file") args = parser.parse_args()

main(args)

Page 28: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def main(args): now = time.time() random.seed(now) rand = random.random() with open(args.output, 'w') as f: f.write(str(rand))

if __name__ == '__main__': parser = ArgumentParser() parser.add_argument('--output', required=True, help="Output file") args = parser.parse_args()

main(args) CLI arguments

can’t control this in tests

Page 29: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def get_seed(): return time.time()

def get_writer(output): if output is None: writer = sys.stdout else: writer = open(output, 'w') return writer

def write_random_to_file(output=None, seed=None): seed = get_seed() if seed is None else seed random.seed(seed) writer = get_writer(output) rand = random.random() with writer as w: w.write(str(rand))

if __name__ == '__main__': … parser.add_argument('--seed', required=False, help="Random seed") args = parser.parse_args()

write_random_to_file(args.output, args.seed)

Page 30: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def get_seed(): return time.time()

def get_writer(output): if output is None: writer = sys.stdout else: writer = open(output, 'w') return writer

def write_random_to_file(output=None, seed=None): seed = get_seed() if seed is None else seed random.seed(seed) writer = get_writer(output) rand = random.random() with writer as w: w.write(str(rand))

if __name__ == '__main__': … parser.add_argument('--seed', required=False, help="Random seed") args = parser.parse_args()

write_random_to_file(args.output, args.seed)

helper functions

Page 31: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def get_seed(): return time.time()

def get_writer(output): if output is None: writer = sys.stdout else: writer = open(output, 'w') return writer

def write_random_to_file(randomizer, writer): rand = randomizer.random() with writer as w: w.write(str(rand))

if __name__ == '__main__': … # Set up default writer and randomizer writer = get_writer(args.output) randomizer = random seed = get_seed() if args.seed is None else args.seed randomizer.seed(seed)

write_random_to_file(randomizer, writer)

Page 32: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

def get_seed(): return time.time()

def get_writer(output): if output is None: writer = sys.stdout else: writer = open(output, 'w') return writer

def write_random_to_file(randomizer, writer): rand = randomizer.random() with writer as w: w.write(str(rand))

if __name__ == '__main__': … # Set up default writer and randomizer writer = get_writer(args.output) randomizer = random seed = get_seed() if args.seed is None else args.seed randomizer.seed(seed)

write_random_to_file(randomizer, writer)

dependency injection

Page 33: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Object-Oriented Design

Page 34: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Single-Responsibility

SOLID

class InputHandler: def __init__(input_data): fmt = self.check_format( input_data ) if fmt == 'CSV': ... else if fmt == 'JSON': ... else: raise TypeError

class CsvHandler: ...

class JsonHandler: ...

Page 35: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Open vs. Closed

SOLID

if x%3 == 0 and x%5 == 0: print("FizzBuzz") elif x%5 == 0: print("Buzz") elif x%3 == 0: print("Fizz") else: print(x)

class FizzBuzz(): def __init__(cls, rules): cls.rules = rules

def say(self,x): for rule in self.rules: if rule.is_handle(x): return rule.say(x)

Page 36: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Liskov Substitution

SOLID

class Rectangle(Shape): def __init__(width, height): self.width = width self.height = height ...

class Square(Rectangle): ...

class Rectangle(Shape): def __init__(width, height): self.shape = Shape(width, height)

class Square(Shape): def __init__(width): height = width self.shape = Shape(width, height)

Page 37: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Interface Segregation

SOLID

class ProcessHandler: def step1(start_data, case): if case == 1: ... else: ...

def step2(step1_output): if case == 1: ... else: ...

def step3(step2_output): if case == 1: ... else: ...

class BaseCaseHandler: def process(data): ...

class ExceptionHandler: def process(data): ...

Page 38: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

Dependency Injection

SOLID

def split_data_sets( data, percent_train ): for datum in data: if random.random() < percent_train: ... else: ...

def split_data_sets( data, randomizer ): for datum in data: if randomizer.set == 'TRAIN': ... else: ...

Page 39: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

SOLID is good for describing systems, but what about prescribing day-to-day

improvements?

Page 40: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

S

T

A

B

L

E

Page 41: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

T

A

B

L

E

Page 42: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

T

A

B

L

E

Page 43: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

Tiny problems first

A

B

L

E

Page 44: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

Tiny problems first

Augment Your Tests

B

L

E

Page 45: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

Tiny problems first

Augment your tests

Back up (sunk costs)

L

E

Page 46: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

Tiny problems first

Augment your tests

Back up (sunk costs)

Leave it better than you found it

E

Page 47: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

STABLE Design

Smell your code

Tiny problems first

Augment your tests

Back up (sunk costs)

Leave it better than you found it

Expect good reasons

Page 48: Testing and Object-Oriented DesignDesign Happens Every Day naming variables choosing function parameters determining class boundaries organizing files structuring projects establishing

References

• github.com/CaitieM20/TheVerificationOfDistributedSystem

• techblog.netflix.com/2011/07/netflix-simian-army.html

• ai.stanford.edu/~zayd/why-is-machine-learning-hard.html

• speakerdeck.com/sarahmei/is-your-code-too-solid-with-transcript