Swenug 2014-04-23
Alexander Tarnowski 1
Testablity for developers
Why testability and testing
for developers?
Unit testing
Maintenance
Continuous integration
Automated acceptance testing
Alexander Tarnowski 2
But…
Alexander Tarnowski 3
Ongoing project
http://leanpub.com/developer_testing
alexander.tarnowski@crisp.se
alexander_tar
blog.crisp.se/author/alexandertarnowskiBlog
www www.techbookreader.com
Blog agilamyter.com
Alexander Tarnowski 5
Testability costs money
Misconception
Alexander Tarnowski 6
Requireme
nts
Specificatio
n
Architectur
al design
Detailed
design
Coding
Unit testing
Integration
testing
System
testing
Acceptance
testing
MisconceptionTesting is a criticizing activity
that can build quality in
Alexander Tarnowski 7
Automated Tools
Manual
Automated
& manual
Q1
Q2 Q3
Q4
Defining testability
Alexander Tarnowski 8
If this were our
system…
Defining testability
Definition 1: Testability = decoupling
Origin: The TDD community
Alexander Tarnowski 9
Defining testability
Definition 2: Testability =
Controllability
Observability
Origin: The testing community
Alexander Tarnowski 10
Defining testability
Definition 3: Testability =
Controllability
Observability
Orthogonality
Deployability
Origin: the ”Developer Testing” book
Alexander Tarnowski 11
Know the driving forces behind
testability
They are:
Controllability
Observability
Orthogonality
Deployability
Make concious decisions to achieve
them
Alexander Tarnowski 12
Programmer wisdom
Fundamental testing
techniques
Alexander Tarnowski 13
Why developers should know
about fundamental testing
techniques
Pass tester tests
Incorporate them in their own tests
Alexander Tarnowski 14
Image: Stuart Miles/FreeDigitalPhotos.net
Equivalence partitioning
0
0.2
0.4
0.6
0.8
1
1.2
1.4
1.6
1.8
0-17
18-23
24-60
60-100
101+
Women
Men
Insurance
premiums
Alexander Tarnowski 15
Typical equivalence
classes
Alexander Tarnowski 16
0-10, 11-23, 24-100
Range
s
0, 3, 6, 9, 12, 15
Defined by
functions
e.g. f(x) = x
mod 3
Sharing a property
e.g. red and blue cars Induction
proofs
Partition data into equivalence
classes
Cover each partition with at least
one test case
Alexander Tarnowski 17
Programmer wisdom
Boundary values
Alexander Tarnowski 18
Ages 24-60
22 23 24 25 26 58 59 60 61 62
Common edge cases
Alexander Tarnowski 19
Numbers
0, -1
Integer.MAX_VALUE/int.MaxValue
n-1, n+1, m-1, m+1
Strings
”” – empty string
null
Max input length
Dates
Precision
Days per month
Different locales
Leap years
Collections
{} – empty collection
null
Indexing → iteration
Boundary values and equivalence
partitions drive the number of
tests
Remember to check boundary
values
Remember to check common edge
cases
Alexander Tarnowski 20
Programmer wisdom
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);
}
Alexander Tarnowski 21
Programmer wisdom
State transition testing
Alexander Tarnowski 22
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
Use for clarifying functionality and
finding missing or incorrect
transitions
Basis for model-based testing
Alexander Tarnowski 23
Programmer wisdom
Decision tables
Alexander Tarnowski 24
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
Conditio
n
Condition
alternative
Action Action entry
Decision tables
Alexander Tarnowski 25
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
Decision tables translate directly
into parameterized/data-driven
tests!
Alexander Tarnowski 26
Programmer wisdom
Anti-testability constructs
Alexander Tarnowski 27
Temporal coupling & state
Alexander Tarnowski 28
var car = new Car();
10 LOC
car.Initialize();
while(itsRaining)
{
50 LOC
}
if (sky.StarsProperlyAligned())
{
car.SetGear(1);
}
30 LOC
car.StartEngine();
car.ReleaseClutch();
Avoid partially initialized objects
Keep temporal coupling in mind
when designing messages and
protocols
Limit state by keeping program
elements small and short-lived
Alexander Tarnowski 29
Programmer wisdom
Indirect input
Direct input (e.g. pure functions):
int DoubleValue(int i)
{
return i * 2;
}
Alexander Tarnowski 30
DateTime.Now.Minute;
new Calculator.Add(10, 10);
using (StreamReader sr = new StreamReader(“Magic.txt"))
{
int.Parse(sr.ReadToEnd());
}
Indirect input
int DoMagic(int i)
{
return i +
}
Indirect input is natural and we
shouldn’t fear it
To cope with indirect input we need
control points and stubs
Alexander Tarnowski 31
Programmer wisdom
Indirect output
Alexander Tarnowski 32
Direct output (i.e. observable through the
public API):
int DoubleValue(int i)
{
return i * 2;
}
Indirect output
bool DoMagic(int i)
{
<insert code here>
return true;
}
Console.Out.WriteLine(i * 42);
RemoteEndpoint.Invoke(42, ”magic”, i);
EventLog.WriteEntry(”MagicApp”, ”value:” + i);
Indirect output is natural and we
shouldn’t fear it
To cope with indirect output we
need observation points and mock
objects
Alexander Tarnowski 33
Programmer wisdom
Domain-to-range ratio
Alexander Tarnowski 34
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
….
Compression
bool IsValidAge(int age)
Alexander Tarnowski 35
82
false
~4*109
true
Use high DRR as a warning flag
Be aware of information loss
Avoid using too large data types
Introduce abstractions (OO/DDD)
Alexander Tarnowski 36
Programmer wisdom
Dependencies
Alexander Tarnowski 37
Relations between objects
Alexander Tarnowski 38
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
Pass in the dependency
Alexander Tarnowski 39
public class ListWrapper
{
private IList<int> wrapped;
public int WrappedListSize
{
get { return wrapped.Count; }
}
public ListWrapper(IList<int> data)
{
wrapped = data;
}
}
Use factory method
Alexander Tarnowski 40
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 };
}
}
Provide external factory or
builder
Alexander Tarnowski 41
public class ListWrapper
{
private IList<int> wrapped;
public int WrappedListSize
{
get { return wrapped.Count; }
}
public ListWrapper(IntegerListBuilder builder)
{
wrapped = builder.Build();
}
}
Alexander Tarnowski 42
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();
}
}
Making code testable is about
making dependencies explicit
Generic strategies for doing so:
Passing them in
Using factory methods
Using factories or builders
Alexander Tarnowski 43
Programmer wisdom
System resource
dependencies
Files
System clock
Sockets, etc
Alexander Tarnowski 44
Solutions
Solution #1:
Your own abstraction
public interface ITimeSource
{
DateTime Now { get; }
}
Alexander Tarnowski 45
Solution #2:
Fakes
using (ShimsContext.Create()) {
System.Fakes.ShimDateTime.NowGet =
() => { return new DateTime(fixedYear, 1, 1); };
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.
Alexander Tarnowski 46
Programmer wisdom
Dependencies between
layers
Alexander Tarnowski 47
Presentation
Business
Data access
Theoretic
al
dependen
cies
Actual
dependen
cies
Clean up messy cross-layer
dependencies
Use Dependency Inversion; depend
on abstractions
Use Dependency Injection
Alexander Tarnowski 48
Programmer wisdom
Duplication
Alexander Tarnowski 49
Copy and paste
public 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 50
Block copy and paste
Alexander Tarnowski 51
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");
}
…
}
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 52
The same method in different
contexts
Alexander Tarnowski 53
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;
}
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 54
in 7/8 places…
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
Alexander Tarnowski 55
Programmer wisdom
Different methods or classes
doing similar things
Ignorance
Fear
Laziness
Choice
Conflict
Alexander Tarnowski 56
Different ways of doing
similar things
Alexander Tarnowski 57
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
Competing domain models
Alexander Tarnowski 58
Image: vectorolie,Danilo Rizzuti/FreeDigitalPhotos.net
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…
Alexander Tarnowski 59
Programmer wisdom
Test-driven development
Alexander Tarnowski 60
Refact
or
Green
Red
Properties of test-driven
code
Alexander Tarnowski 61
Code
driven by
tests
Executabl
e in
isolation
Small
Does one
thing
Indirect
creation
Pure
functions
separete
d from
side
effects
• Drives design, not correctness
• Negative tests are not used
• There are two kinds of TDD
Classic TDD London school
TDD and testability
Alexander Tarnowski
Supplement tests that drive design
with tests that drive correctness!
Alexander Tarnowski 63
Programmer wisdom
Summary
Alexander Tarnowski 64
Testability is a skill
Developers do supporting
technical testing
Testability:
 Controllability
 Observability
 Orthogonality
 Deployability
Testing techniques
Anti-testability const
 Temporal coupling
 Indirect input
 Indirect output
 High DRR
Duplication:
 Mechanical
 Mental
TDD drives design
not correctness!
Dependencies:
 Pass in
 Factory methods
 Factories/Builders
 Fakes

Testability for Developers

  • 1.
    Swenug 2014-04-23 Alexander Tarnowski1 Testablity for developers
  • 2.
    Why testability andtesting for developers? Unit testing Maintenance Continuous integration Automated acceptance testing Alexander Tarnowski 2
  • 3.
  • 4.
  • 5.
    Alexander Tarnowski 5 Testabilitycosts money Misconception
  • 6.
    Alexander Tarnowski 6 Requireme nts Specificatio n Architectur aldesign Detailed design Coding Unit testing Integration testing System testing Acceptance testing MisconceptionTesting is a criticizing activity that can build quality in
  • 7.
    Alexander Tarnowski 7 AutomatedTools Manual Automated & manual Q1 Q2 Q3 Q4
  • 8.
    Defining testability Alexander Tarnowski8 If this were our system…
  • 9.
    Defining testability Definition 1:Testability = decoupling Origin: The TDD community Alexander Tarnowski 9
  • 10.
    Defining testability Definition 2:Testability = Controllability Observability Origin: The testing community Alexander Tarnowski 10
  • 11.
    Defining testability Definition 3:Testability = Controllability Observability Orthogonality Deployability Origin: the ”Developer Testing” book Alexander Tarnowski 11
  • 12.
    Know the drivingforces behind testability They are: Controllability Observability Orthogonality Deployability Make concious decisions to achieve them Alexander Tarnowski 12 Programmer wisdom
  • 13.
  • 14.
    Why developers shouldknow about fundamental testing techniques Pass tester tests Incorporate them in their own tests Alexander Tarnowski 14 Image: Stuart Miles/FreeDigitalPhotos.net
  • 15.
  • 16.
    Typical equivalence classes Alexander Tarnowski16 0-10, 11-23, 24-100 Range s 0, 3, 6, 9, 12, 15 Defined by functions e.g. f(x) = x mod 3 Sharing a property e.g. red and blue cars Induction proofs
  • 17.
    Partition data intoequivalence classes Cover each partition with at least one test case Alexander Tarnowski 17 Programmer wisdom
  • 18.
    Boundary values Alexander Tarnowski18 Ages 24-60 22 23 24 25 26 58 59 60 61 62
  • 19.
    Common edge cases AlexanderTarnowski 19 Numbers 0, -1 Integer.MAX_VALUE/int.MaxValue n-1, n+1, m-1, m+1 Strings ”” – empty string null Max input length Dates Precision Days per month Different locales Leap years Collections {} – empty collection null Indexing → iteration
  • 20.
    Boundary values andequivalence partitions drive the number of tests Remember to check boundary values Remember to check common edge cases Alexander Tarnowski 20 Programmer wisdom
  • 21.
    Use parameterized teststo 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); } Alexander Tarnowski 21 Programmer wisdom
  • 22.
    State transition testing AlexanderTarnowski 22 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
  • 23.
    Use for clarifyingfunctionality and finding missing or incorrect transitions Basis for model-based testing Alexander Tarnowski 23 Programmer wisdom
  • 24.
    Decision tables Alexander Tarnowski24 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 Conditio n Condition alternative Action Action entry
  • 25.
    Decision tables Alexander Tarnowski25 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
  • 26.
    Decision tables translatedirectly into parameterized/data-driven tests! Alexander Tarnowski 26 Programmer wisdom
  • 27.
  • 28.
    Temporal coupling &state Alexander Tarnowski 28 var car = new Car(); 10 LOC car.Initialize(); while(itsRaining) { 50 LOC } if (sky.StarsProperlyAligned()) { car.SetGear(1); } 30 LOC car.StartEngine(); car.ReleaseClutch();
  • 29.
    Avoid partially initializedobjects Keep temporal coupling in mind when designing messages and protocols Limit state by keeping program elements small and short-lived Alexander Tarnowski 29 Programmer wisdom
  • 30.
    Indirect input Direct input(e.g. pure functions): int DoubleValue(int i) { return i * 2; } Alexander Tarnowski 30 DateTime.Now.Minute; new Calculator.Add(10, 10); using (StreamReader sr = new StreamReader(“Magic.txt")) { int.Parse(sr.ReadToEnd()); } Indirect input int DoMagic(int i) { return i + }
  • 31.
    Indirect input isnatural and we shouldn’t fear it To cope with indirect input we need control points and stubs Alexander Tarnowski 31 Programmer wisdom
  • 32.
    Indirect output Alexander Tarnowski32 Direct output (i.e. observable through the public API): int DoubleValue(int i) { return i * 2; } Indirect output bool DoMagic(int i) { <insert code here> return true; } Console.Out.WriteLine(i * 42); RemoteEndpoint.Invoke(42, ”magic”, i); EventLog.WriteEntry(”MagicApp”, ”value:” + i);
  • 33.
    Indirect output isnatural and we shouldn’t fear it To cope with indirect output we need observation points and mock objects Alexander Tarnowski 33 Programmer wisdom
  • 34.
    Domain-to-range ratio Alexander Tarnowski34 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 ….
  • 35.
    Compression bool IsValidAge(int age) AlexanderTarnowski 35 82 false ~4*109 true
  • 36.
    Use high DRRas a warning flag Be aware of information loss Avoid using too large data types Introduce abstractions (OO/DDD) Alexander Tarnowski 36 Programmer wisdom
  • 37.
  • 38.
    Relations between objects AlexanderTarnowski 38 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
  • 39.
    Pass in thedependency Alexander Tarnowski 39 public class ListWrapper { private IList<int> wrapped; public int WrappedListSize { get { return wrapped.Count; } } public ListWrapper(IList<int> data) { wrapped = data; } }
  • 40.
    Use factory method AlexanderTarnowski 40 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 }; } }
  • 41.
    Provide external factoryor builder Alexander Tarnowski 41 public class ListWrapper { private IList<int> wrapped; public int WrappedListSize { get { return wrapped.Count; } } public ListWrapper(IntegerListBuilder builder) { wrapped = builder.Build(); } }
  • 42.
    Alexander Tarnowski 42 publicclass 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(); } }
  • 43.
    Making code testableis about making dependencies explicit Generic strategies for doing so: Passing them in Using factory methods Using factories or builders Alexander Tarnowski 43 Programmer wisdom
  • 44.
  • 45.
    Solutions Solution #1: Your ownabstraction public interface ITimeSource { DateTime Now { get; } } Alexander Tarnowski 45 Solution #2: Fakes using (ShimsContext.Create()) { System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
  • 46.
    There isn’t asingle 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. Alexander Tarnowski 46 Programmer wisdom
  • 47.
    Dependencies between layers Alexander Tarnowski47 Presentation Business Data access Theoretic al dependen cies Actual dependen cies
  • 48.
    Clean up messycross-layer dependencies Use Dependency Inversion; depend on abstractions Use Dependency Injection Alexander Tarnowski 48 Programmer wisdom
  • 49.
  • 50.
    Copy and paste publicclass 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 50
  • 51.
    Block copy andpaste Alexander Tarnowski 51 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"); } … }
  • 52.
    Constructor copy and paste publicNetworkInterface(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 52
  • 53.
    The same methodin different contexts Alexander Tarnowski 53 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; }
  • 54.
    Divergent duplicated methods indifferent 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 54 in 7/8 places…
  • 55.
    Code that’s beenmechanically copied and pasted increases the size of the code base and is annoying… … but the true danger lies in convergence Alexander Tarnowski 55 Programmer wisdom
  • 56.
    Different methods orclasses doing similar things Ignorance Fear Laziness Choice Conflict Alexander Tarnowski 56
  • 57.
    Different ways ofdoing similar things Alexander Tarnowski 57 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
  • 58.
    Competing domain models AlexanderTarnowski 58 Image: vectorolie,Danilo Rizzuti/FreeDigitalPhotos.net
  • 59.
    Understand that mental duplicationmakes 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… Alexander Tarnowski 59 Programmer wisdom
  • 60.
  • 61.
    Properties of test-driven code AlexanderTarnowski 61 Code driven by tests Executabl e in isolation Small Does one thing Indirect creation Pure functions separete d from side effects
  • 62.
    • Drives design,not correctness • Negative tests are not used • There are two kinds of TDD Classic TDD London school TDD and testability Alexander Tarnowski
  • 63.
    Supplement tests thatdrive design with tests that drive correctness! Alexander Tarnowski 63 Programmer wisdom
  • 64.
    Summary Alexander Tarnowski 64 Testabilityis a skill Developers do supporting technical testing Testability:  Controllability  Observability  Orthogonality  Deployability Testing techniques Anti-testability const  Temporal coupling  Indirect input  Indirect output  High DRR Duplication:  Mechanical  Mental TDD drives design not correctness! Dependencies:  Pass in  Factory methods  Factories/Builders  Fakes

Editor's Notes

  • #6 Testbarhet är inte om pengar. Det är om skills!
  • #11 Controllability = settingstate, configuringFormal definitions: A software component is observable “if distinct outputs are generated from distinct inputs”A software component is controllable “if, given any desired output value, an extra input exists which ‘forces’ the component output to that value”
  • #20 Max input length, especially true for buffers that can get overrunPrecision = minutes, seconds, milliseconds, etc
  • #23 Not the complete diagram!Got many aha-moments whendrawing the diagram.Didn’tknowthat the timer couldcountupwards.
  • #35 f(0) borde rimligtvis ge 1…f(10) ej definierad
  • #48 Dependencyinjection!
  • #50 There’s just moreofeverything.Itmessesup the metricsMechnical and mental
  • #52 Block copy and paste is the inverseofExtractMethod
  • #54 Yes, thismethodappearedduplicated 8 times