Transcript
Page 1: Testability for Developers

Alexander Tarnowski 1

Swenug 2014-04-23

Testablity for developers

Page 2: Testability for Developers

2

Why testability and testing for developers?

Unit testingMaintenanceContinuous integrationAutomated acceptance testing

Alexander Tarnowski

Page 3: Testability for Developers

3

But…

Alexander Tarnowski

Page 4: Testability for Developers

Ongoing project

http://leanpub.com/developer_testing

[email protected]

alexander_tar

blog.crisp.se/author/alexandertarnowskiBlog

www

www.techbookreader.comBlog agilamyter.com

Page 5: Testability for Developers

5Alexander Tarnowski

Testability costs moneyM

isco

ncep

tion

Page 6: Testability for Developers

6Alexander Tarnowski

RequirementsSpecificati

onArchitectural design

Detaileddesign

Coding

Unit testing

Integration

testing

Systemtesting

Acceptance

testing

Mis

conc

epti

onTesting is a criticizing activity

that can build quality in

Page 7: Testability for Developers

7Alexander Tarnowski

Automated Tools

ManualAutomated& manual

Q1Q2 Q3

Q4

Page 8: Testability for Developers

8

Defining testability

Alexander Tarnowski

The Black Box

If this were our system…

Page 9: Testability for Developers

9

Defining testabilityDefinition 1: Testability = decoupling

Origin: The TDD community

Alexander Tarnowski

Page 10: Testability for Developers

10

Defining testabilityDefinition 2: Testability = Controllability Observability

Origin: The testing community

Alexander Tarnowski

Page 11: Testability for Developers

11

Defining testabilityDefinition 3: Testability = ControllabilityObservabilityOrthogonalityDeployability

Origin: the ”Developer Testing” book

Alexander Tarnowski

Page 12: Testability for Developers

Alexander Tarnowski 12

Know the driving forces behind testabilityThey are:ControllabilityObservabilityOrthogonalityDeployability

Make concious decisions to achieve them

Programmer wisdom

Page 13: Testability for Developers

13

Fundamental testing techniques

Alexander Tarnowski

Page 14: Testability for Developers

14

Why developers should know about fundamental testing

techniquesPass tester testsIncorporate them in their own tests

Alexander TarnowskiImage: Stuart Miles/FreeDigitalPhotos.net

Page 15: Testability for Developers

Alexander Tarnowski 15

Equivalence partitioning

0-17 18-23 24-60 60-100 101+

0

0.2

0.4

0.6

0.8

11.2

1.4

1.6

1.8

WomenMen

Insurance premiums

Page 16: Testability for Developers

Alexander Tarnowski 16

Typical equivalence classes

0-10, 11-23, 24-100

Ranges

0, 3, 6, 9, 12, 15

Defined by functions

e.g. f(x) = x mod 3

Sharing a propertye.g. red and blue cars Induction

proofs

Page 17: Testability for Developers

Alexander Tarnowski 17

Partition data into equivalence classes

Cover each partition with at least one test case

Programmer wisdom

Page 18: Testability for Developers

18

Boundary values

Alexander Tarnowski

Ages 24-60

22 23 24 25 26 58 59 60 61 62

Page 19: Testability for Developers

19

Common edge cases

Alexander Tarnowski

Numbers0, -1

Integer.MAX_VALUE/int.MaxValuen-1, n+1, m-1, m+1

Strings”” – empty string

nullMax input length

DatesPrecision

Days per monthDifferent locales

Leap years

Collections{} – empty collection

nullIndexing → iteration

Page 20: Testability for Developers

Alexander Tarnowski 20

Boundary values and equivalence partitions drive the number of tests

Remember to check boundary valuesRemember to check common edge

cases

Programmer wisdom

Page 21: Testability for Developers

Alexander Tarnowski 21

Use parameterized tests to capture boundary values

[TestCase(18, Gender.Male, Result = 1.75)][TestCase(23, Gender.Male, Result = 1.75)][TestCase(24, Gender.Male, Result = 1.0)]// ...public double VerifyPremiumFactor(int age, Gender gender){ return new PremiumRuleEngine().GetPremiumFactor(age, gender);}

Programmer wisdom

Page 22: Testability for Developers

22

State transition testing

Alexander Tarnowski

Start

Reset

Press min+sec

Setting timePress min

Press min

Press sec

Counting down

Press min+sec

Press start/stop

Press start/stop

Press min+sec

Press min+sec

Counting up

Press start/stop

Press min+sec

Page 23: Testability for Developers

Alexander Tarnowski 23

Use for clarifying functionality and finding missing or incorrect transitions

Basis for model-based testing

Programmer wisdom

Page 24: Testability for Developers

24

Decision tables

Alexander Tarnowski

Age 18-23 18-23 24-59 24-59 60+ 60+

Gender Male Female

Male Female Male Female

Premium factor 1.0 N N N Y N N

Premium factor 1.05 N N Y N N N

Premium factor 1.25 N N N N N Y

Premium factor 1.35 N N N N Y N

Premium factor 1.65 N Y N N N N

Premium factor 1.75 Y N N N N N

Condition

Condition alternative

Action Action entry

Page 25: Testability for Developers

25

Decision tables

Alexander Tarnowski

Age 18-23 18-23 24-59 24-59 60+ 60+

Gender Male Female

Male Female Male Female

Premium factor 1.75 1.65 1.05 1 1.35 1.25

Page 26: Testability for Developers

Alexander Tarnowski 26

Decision tables translate directly into parameterized/data-driven tests!

Programmer wisdom

Page 27: Testability for Developers

27

Anti-testability constructs

Alexander Tarnowski

Page 28: Testability for Developers

28

Temporal coupling & state

Alexander Tarnowski

var car = new Car();10 LOCcar.Initialize();while(itsRaining) { 50 LOC}if (sky.StarsProperlyAligned()) { car.SetGear(1);}30 LOCcar.StartEngine();car.ReleaseClutch();

Page 29: Testability for Developers

Alexander Tarnowski 29

Avoid partially initialized objectsKeep temporal coupling in mind

when designing messages and protocols

Limit state by keeping program elements small and short-lived

Programmer wisdom

Page 30: Testability for Developers

30

Indirect inputDirect input (e.g. pure functions):int DoubleValue(int i) { return i * 2;}

Alexander Tarnowski

DateTime.Now.Minute;new Calculator.Add(10, 10);using (StreamReader sr = new StreamReader(“Magic.txt")){ int.Parse(sr.ReadToEnd()); }

Indirect inputint DoMagic(int i) { return i +}

Page 31: Testability for Developers

Alexander Tarnowski 31

Indirect input is natural and we shouldn’t fear it

To cope with indirect input we need control points and stubs

Programmer wisdom

Page 32: Testability for Developers

32

Indirect output

Alexander Tarnowski

Direct output (i.e. observable through the public API):int DoubleValue(int i) { return i * 2;}

Indirect outputbool DoMagic(int i) { <insert code here> return true;}

Console.Out.WriteLine(i * 42);RemoteEndpoint.Invoke(42, ”magic”, i);EventLog.WriteEntry(”MagicApp”, ”value:” + i);

Page 33: Testability for Developers

Alexander Tarnowski 33

Indirect output is natural and we shouldn’t fear it

To cope with indirect output we need observation points and mock objects

Programmer wisdom

Page 34: Testability for Developers

34

Domain-to-range ratio

Alexander Tarnowski

Input values(domain, D)

Output values

(range, R)

Correct computation

Faulty computation

DRR = |D| / |R|

f(0) 0

f(1-9) 1

f(11-99) 2

f(100-999) 3

f(1000-9999) 4

….

Page 35: Testability for Developers

35

Compressionbool IsValidAge(int age)

Alexander Tarnowski

82

false

~4*109

true

Page 36: Testability for Developers

Alexander Tarnowski 36

Use high DRR as a warning flagBe aware of information lossAvoid using too large data typesIntroduce abstractions (OO/DDD)

Programmer wisdom

Page 37: Testability for Developers

37

Dependencies

Alexander Tarnowski

Page 38: Testability for Developers

38

Relations between objects

Alexander Tarnowski

public class ListWrapper{ private IList<int> wrapped; public int WrappedListSize { get { return wrapped.Count; } } public ListWrapper() { wrapped = new List<int> { 1, 2, 3 }; }} Indirect input

Legacy

code 101

Page 39: Testability for Developers

39

Pass in the dependency

Alexander Tarnowski

public class ListWrapper{ private IList<int> wrapped; public int WrappedListSize { get { return wrapped.Count; } } public ListWrapper(IList<int> data) { wrapped = data; }}

Page 40: Testability for Developers

40

Use factory method

Alexander Tarnowski

public class ListWrapper{ private IList<int> wrapped; public int WrappedListSize { get { return wrapped.Count; } } public ListWrapper() { wrapped = CreateWrappedList(); } protected virtual IList<int> CreateWrappedList() { return new List<int> { 1, 2, 3 }; }}

Page 41: Testability for Developers

41

Provide external factory or builder

Alexander Tarnowski

public class ListWrapper{ private IList<int> wrapped; public int WrappedListSize { get { return wrapped.Count; } } public ListWrapper(IntegerListBuilder builder) { wrapped = builder.Build(); } }

Page 42: Testability for Developers

42Alexander Tarnowski

public class IntegerListBuilder{ private int startingAt = 1; private int endingWith = 10; public IntegerListBuilder StartingAt(int startingAt) { this.startingAt = startingAt; return this; } public IntegerListBuilder EndingWith(int endingWith) { this.endingWith = endingWith; return this; } public IList<int> Build() { return Enumerable.Range(startingAt, endingWith).ToList(); }}

Page 43: Testability for Developers

Alexander Tarnowski 43

Making code testable is about making dependencies explicit

Generic strategies for doing so:Passing them in Using factory methodsUsing factories or builders

Programmer wisdom

Page 44: Testability for Developers

44

System resource dependencies

FilesSystem clockSockets, etc

Alexander Tarnowski

Page 45: Testability for Developers

45

SolutionsSolution #1: Your own abstractionpublic interface ITimeSource{ DateTime Now { get; }}

Alexander Tarnowski

Solution #2: Fakesusing (ShimsContext.Create()) { System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };

Page 46: Testability for Developers

Alexander Tarnowski 46

There isn’t a single problem in computer science that cannot be solved by adding another layer of indirection. This is certainly true of system resoruce dependencies.

Use Fakes if there’s no other way. But first try without.

Programmer wisdom

Page 47: Testability for Developers

47

Dependencies between layers

Alexander Tarnowski

PresentationBusiness

Data access

Theoretical dependenciesActual

dependencies

Page 48: Testability for Developers

Alexander Tarnowski 48

Clean up messy cross-layer dependencies

Use Dependency Inversion; depend on abstractions

Use Dependency Injection

Programmer wisdom

Page 49: Testability for Developers

49

Duplication

Alexander Tarnowski

?

Page 50: Testability for Developers

50

Copy and pastepublic class CustomerRepository{ public void Create(Customer customer) { if (customer.Gender == Gender.Unknown || !customer.YearOfBirth.HasValue) { throw new ArgumentException("Incomplete customer"); } … }

public void Update(Customer customer) { if (customer.Gender == Gender.Unknown || !customer.YearOfBirth.HasValue) { throw new ArgumentException("Incomplete customer"); } … }

Alexander Tarnowski

Page 51: Testability for Developers

51

Block copy and paste

Alexander Tarnowski

public class CustomerRepository{ public void Create(Customer customer) { if (customer.Gender == Gender.Unknown || !customer.YearOfBirth.HasValue) { throw new ArgumentException("Incomplete customer"); }

if (DateTime.Now.Year - customer.YearOfBirth.Value < 18) { throw new ArgumentOutOfRangeException("Underage customer"); } … }

public void Update(Customer customer) { if (customer.Gender == Gender.Unknown || !customer.YearOfBirth.HasValue) { throw new ArgumentException("Incomplete customer"); }

if (DateTime.Now.Year - customer.YearOfBirth.Value < 18) { throw new ArgumentOutOfRangeException("Underage customer"); } … }

Page 52: Testability for Developers

52

Constructor copy and paste

public NetworkInterface(IPAddress ipAddress, NetMask netMask, IPAddress broadcast, IPAddress defaultRoute){ this.ipAddress = ipAddress; this.netMask = netMask; this.broadcast = broadcast; this.defaultRoute = defaultRoute;}

public NetworkInterface(IPAddress ipV6Address, NetMaskIpV6 ipV6NetMask, IPAddress ipV6DefaultRoute){ this.ipV6Address = ipV6Address; this.ipV6NetMask = ipV6NetMask; this.ipV6DefaultRoute = ipV6DefaultRoute;}

public NetworkInterface(IPAddress ipAddress, NetMask netMask, IPAddress broadcast, IPAddress defaultRoute,IPAddress ipV6Address, NetMaskIpV6 ipV6NetMask, IPAddress ipV6DefaultRoute)

{ this.ipAddress = ipAddress; this.netMask = netMask; this.broadcast = broadcast; this.defaultRoute = defaultRoute; this.ipV6Address = ipV6Address; this.ipV6NetMask = ipV6NetMask; this.ipV6DefaultRoute = ipV6DefaultRoute;}

Alexander Tarnowski

Page 53: Testability for Developers

53

The same method in different contexts

Alexander Tarnowski

x 8

public static long DiffTime(DateTime t1, DateTime t2){ if (t1.Date != t2.Date) { throw new ArgumentException("Incomparable dates"); } return (t2.Hour - t1.Hour) * 60;}

Page 54: Testability for Developers

54

Divergent duplicated methods in different contexts

public static long DiffTime(DateTime t1, DateTime t2){ if (t1.Date != t2.Date) { throw new ArgumentException("Incomparable dates"); } return (t2.Hour * 60 + t2.Minute) - (t1.Hour * 60 + t1.Minute);}

Alexander Tarnowski

in 7/8 places…

Page 55: Testability for Developers

Alexander Tarnowski 55

Code that’s been mechanically copied and pasted increases the size of the code base and is annoying…

… but the true danger lies in convergence

Programmer wisdom

Page 56: Testability for Developers

56

Different methods or classes doing similar things

Ignorance FearLaziness ChoiceConflict

Alexander Tarnowski

Page 57: Testability for Developers

57

Different ways of doing similar things

Alexander Tarnowski

Module Alpha uses… Module Bravo uses…

Logging framework Apple Logging framework Banana

Hand-written SQL An O/R mapper

A date/time library Time computations implemented ”by hand”

Client-side validation Server-side validation

Page 58: Testability for Developers

58

Competing domain models

Alexander TarnowskiImage: vectorolie,Danilo Rizzuti/FreeDigitalPhotos.net

Page 59: Testability for Developers

Alexander Tarnowski 59

Understand that mental duplication makes testing, understanding, and sometimes even recruiting harder

Have a long-term strategy for dealing with mental duplication

Duplication makes things ”hard”. If it’s hard then…

Programmer wisdom

Page 60: Testability for Developers

60

Test-driven development

Alexander Tarnowski

Refactor

Green

Red

Page 61: Testability for Developers

61

Properties of test-driven code

Alexander Tarnowski

Code driven

by tests

Executable in

isolation

Small

Does one thing

Indirect creation

Pure function

s separeted from side

effects

Page 62: Testability for Developers

• Drives design, not correctness• Negative tests are not used• There are two kinds of TDD

Classic TDD London school

TDD and testability

Alexander Tarnowski

Page 63: Testability for Developers

Alexander Tarnowski 63

Supplement tests that drive design with tests that drive correctness!

Programmer wisdom

Page 64: Testability for Developers

64

Summary

Alexander Tarnowski

Testability is a skill

Developers do supporting technical testing

The Black Box

Testability: Controllability Observability Orthogonality Deployability

Testing techniques

Anti-testability constructs Temporal coupling Indirect input Indirect output High DRR

Duplication: Mechanical Mental TDD drives design,

not correctness!

Dependencies: Pass in Factory methods Factories/Builders Fakes


Top Related