SlideShare a Scribd company logo
Programming with GUTs 
@KevlinHenney 
kevlin@curbralan.com
Programming with GUTs
When you write unit tests, TDD- style or after your development, you scrutinize, you think, and often you prevent problems without even encountering a test failure. 
Michael Feathers "The Flawed Theory Behind Unit Testing" http://michaelfeathers.typepad.com/michael_feathers_blog/2008/06/the-flawed-theo.html
Very many people say "TDD" when they really mean, "I have good unit tests" ("I have GUTs"?). 
Ron Jeffries tried for years to explain what this was, but we never got a catch-phrase for it, and now TDD is being watered down to mean GUTs. 
Alistair Cockburn "The modern programming professional has GUTs" http://alistair.cockburn.us/The+modern+programming+professional+has+GUTs
size_t ints_to_csv( 
const int * to_write, size_t how_many, 
char * output, size_t length);
size_t ints_to_csv( 
const int * to_write, size_t how_many, char * output, size_t length) 
{ 
size_t result = 0; 
if(length != 0) 
{ 
if(how_many == 0) 
{ 
output[0] = '0'; 
} 
else 
{ 
for(size_t which = 0; which != how_many && result != length; ++which) 
{ 
result += 
snprintf( 
output + result, length - result, 
which == 0 ? "%i" : ",%i", 
to_write[which]); 
} 
result = result > length - 1 ? length - 1 : result; 
} 
} 
return result; 
}
extern "C" size_t ints_to_csv( 
const int * to_write, size_t how_many, char * output, size_t length) 
{ 
size_t result = 0; 
if(length != 0) 
{ 
output[length - 1] = '0'; 
std::ostrstream buffer(output, length - 1); 
for(size_t which = 0; which != how_many; ++which) 
buffer << (which == 0 ? "" : ",") << to_write[which]; 
buffer << std::ends; 
result = std::strlen(output); 
} 
return result; 
}
function 
test
void test_ints_to_csv() 
{ 
size_t written = ints_to_csv(NULL, 0, NULL, 0); 
assert(written == 0); 
const int input[] = { 42 }; 
written = ints_to_csv(input, 1, NULL, 0); 
assert(written == 0); 
char output[3] = "+++"; 
written = ints_to_csv(NULL, 0, output, sizeof output); 
assert(written == 0); 
assert(output[0] == '0'); 
memcpy(output, "+++", sizeof output); 
written = ints_to_csv(input, 1, output, sizeof output); 
assert(written == 2); 
assert(strcmp(output, "42") == 0); 
... 
}
void test_ints_to_csv() 
{ 
// no values from null to null output writes nothing 
size_t written = ints_to_csv(NULL, 0, NULL, 0); 
assert(written == 0); 
// value to null output writes nothing 
const int input[] = { 42 }; 
written = ints_to_csv(input, 1, NULL, 0); 
assert(written == 0); 
// no values to sufficient output writes empty 
char output[3] = "+++"; 
written = ints_to_csv(NULL, 0, output, sizeof output); 
assert(written == 0); 
assert(output[0] == '0'); 
// positive value to sufficient output writes value without sign 
memcpy(output, "+++", sizeof output); 
written = ints_to_csv(input, 1, output, sizeof output); 
assert(written == 2); 
assert(strcmp(output, "42") == 0); 
... 
}
void test_ints_to_csv() 
{ 
// no values from null to null output writes nothing 
{ 
size_t written = ints_to_csv(NULL, 0, NULL, 0); 
assert(written == 0); 
} 
// value to null output writes nothing 
{ 
const int input[] = { 42 }; 
size_t written = ints_to_csv(input, 1, NULL, 0); 
assert(written == 0); 
} 
// no values to sufficient output writes empty 
{ 
char output[3] = "+++"; 
size_t written = ints_to_csv(NULL, 0, output, sizeof output); 
assert(written == 0); 
assert(output[0] == '0'); 
} 
// positive value to sufficient output writes value without sign 
{ 
const int input[] = { 42 }; 
char output[3] = "+++"; 
size_t written = ints_to_csv(input, 1, output, sizeof output); 
assert(written == 2); 
assert(strcmp(output, "42") == 0); 
}
void no_values_from_null_to_null_output_writes_nothing() 
{ 
size_t written = ints_to_csv(NULL, 0, NULL, 0); 
assert(written == 0); 
} 
void value_to_null_output_writes_nothing() 
{ 
const int input[] = { 42 }; 
size_t written = ints_to_csv(input, 1, NULL, 0); 
assert(written == 0); 
} 
void no_values_to_sufficient_output_writes_empty() 
{ 
char output[3] = "+++"; 
size_t written = ints_to_csv(NULL, 0, output, sizeof output); 
assert(written == 0); 
assert(output[0] == '0'); 
} 
void positive_value_to_sufficient_output_writes_value_without_sign() 
{ 
const int input[] = { 42 }; 
char output[3] = "+++"; 
size_t written = ints_to_csv(input, 1, output, sizeof output); 
assert(written == 2); 
assert(strcmp(output, "42") == 0); 
} 
void negative_value_to_sufficient_output_writes_value_with_sign() 
{ 
const int input[] = { -42 }; 
char output[4] = "++++"; 
size_t written = ints_to_csv(input, 1, output, sizeof output); 
assert(written == 3); 
assert(strcmp(output, "-42") == 0); 
} 
void value_to_insufficient_output_writes_truncated_value() 
{ 
const int input[] = { 42 }; 
char output[2] = "++"; 
size_t written = ints_to_csv(input, 1, output, sizeof output); 
assert(written == 1); 
assert(strcmp(output, "4") == 0); 
} 
void multiple_values_to_sufficient_output_writes_comma_separated_values() 
{ 
const int input[] = { 42, -273, 0, 7 }; 
char output[12] = "++++++++++++"; 
size_t written = ints_to_csv(input, 4, output, sizeof output); 
assert(written == 11); 
assert(strcmp(output, "42,-273,0,7") == 0); 
} 
void multiple_values_to_insufficient_output_writes_truncated_value_sequence() 
{ 
const int input[] = { 42, -273, 0, 7 }; 
char output[9] = "+++++++++"; 
size_t written = ints_to_csv(input, 4, output, sizeof output); 
assert(written == 8); 
assert(strcmp(output, "42,-273,") == 0); 
}
void no_values_from_null_to_null_output_writes_nothing() 
{ 
... 
} 
void value_to_null_output_writes_nothing() 
{ 
... 
} 
void no_values_to_sufficient_output_writes_empty() 
{ 
... 
} 
void positive_value_to_sufficient_output_writes_value_without_sign() 
{ 
... 
} 
void negative_value_to_sufficient_output_writes_value_with_sign() 
{ 
... 
} 
void value_to_insufficient_output_writes_truncated_value() 
{ 
... 
} 
void multiple_values_to_sufficient_output_writes_comma_separated_values() 
{ 
... 
} 
void multiple_values_to_insufficient_output_writes_truncated_value_sequence() 
{ 
... 
}
function 
test 
test 
test
size_t ints_to_csv( const int * to_write, size_t how_many, char * output, size_t length); 
 No values from null to null output writes nothing 
 Value to null output writes nothing 
 No values to sufficient output writes empty 
 Positive value to sufficient output writes value without sign 
 Negative value to sufficient output writes value with sign 
 Value to insufficient output writes truncated value 
 Multiple values to sufficient output writes comma separated values 
 Multiple values to insufficient output writes truncated value sequence
Tests that are not written with their role as specifications in mind can be very confusing to read. The difficulty in understanding what they are testing can greatly reduce the velocity at which a codebase can be changed. 
Nat Pryce and Steve Freeman "Are Your Tests Really Driving Your Development?"
Programming with GUTs
Propositions are vehicles for stating how things are or might be.
Thus only indicative sentences which it makes sense to think of as being true or as being false are capable of expressing propositions.
public static boolean isLeapYear(int year) ...
yearsNotDivisibleBy4... 
yearsDivisibleBy4ButNotBy100... 
yearsDivisibleBy100ButNotBy400... 
yearsDivisibleBy400...
Years_not_divisible_by_4_... 
Years_divisible_by_4_but_not_by_100_... 
Years_divisible_by_100_but_not_by_400_... 
Years_divisible_by_400_...
Years_not_divisible_by_4_should_not_be_leap_years 
Years_divisible_by_4_but_not_by_100_should_be_leap_years 
Years_divisible_by_100_but_not_by_400_should_not_be_leap_years 
Years_divisible_by_400_should_be_leap_years
Programming with GUTs
Years_not_divisible_by_4_are_not_leap_years 
Years_divisible_by_4_but_not_by_100_are_leap_years 
Years_divisible_by_100_but_not_by_400_are_not_leap_years 
Years_divisible_by_400_are_not_leap_years
Years_not_divisible_by_4_are_not_leap_years 
Years_divisible_by_4_but_not_by_100_are_leap_years 
Years_divisible_by_100_but_not_by_400_are_not_leap_years 
Years_divisible_by_400_are_not_leap_years
Years_not_divisible_by_4_are_not_leap_years 
Years_divisible_by_4_but_not_by_100_are_leap_years 
Years_divisible_by_100_but_not_by_400_are_not_leap_years 
Years_divisible_by_400_are_not_leap_years
A test case should be just that: it should correspond to a single case.
public class Leap_year_spec 
{ 
public static class A_year_is_a_leap_year 
{ 
@Test public void If_it_is_divisible_by_4_but_not_by_100()  
@Test public void If_it_is_divisible_by_400()  
} 
public static class A_year_is_not_a_leap_year 
{ 
@Test public void If_it_is_not_divisible_by_4()  
@Test public void If_it_is_divisible_by_100_but_not_by_400()  
} 
}
public class Leap_year_spec 
{ 
public static class A_year_is_a_leap_year 
{ 
@Test public void If_it_is_divisible_by_4_but_not_by_100()  
@Test public void If_it_is_divisible_by_400()  
} 
public static class A_year_is_not_a_leap_year 
{ 
@Test public void If_it_is_not_divisible_by_4()  
@Test public void If_it_is_divisible_by_100_but_not_by_400()  
} 
}
method 
test 
test 
test 
method 
method
public class RecentlyUsedList 
{ 
... 
public RecentlyUsedList() ... 
public int Count 
{ 
get... 
} 
public string this[int index] 
{ 
get... 
} 
public void Add(string newItem) ... 
... 
}
[TestFixture] 
public class RecentlyUsedListTests 
{ 
[Test] 
public void TestConstructor() ... 
[Test] 
public void TestCountGet() ... 
[Test] 
public void TestIndexerGet() ... 
[Test] 
public void TestAdd() ... 
... 
}
method 
test 
test 
test 
method 
method 
test 
test
namespace RecentlyUsedList_spec 
{ 
[TestFixture] 
public class A_new_list 
{ 
[Test] public void Is_empty()  
} 
[TestFixture] 
public class An_empty_list 
{ 
[Test] public void Retains_a_single_addition()  
[Test] public void Retains_unique_additions_in_stack_order()  
} 
[TestFixture] 
public class A_non_empty_list 
{ 
[Test] public void Is_unchanged_when_head_item_is_readded()  
[Test] public void Moves_non_head_item_to_head_when_it_is_readded()  
} 
[TestFixture] 
public class Any_list 
{ 
[Test] public void Rejects_addition_of_null_items()  
[Test] public void Rejects_indexing_past_its_end()  
[Test] public void Rejects_negative_indexing()  
} 
}
namespace RecentlyUsedList_spec { [TestFixture] public class A_new_list { [Test] public void Is_empty()  } [TestFixture] public class An_empty_list { [Test] public void Retains_a_single_addition()  [Test] public void Retains_unique_additions_in_stack_order()  } [TestFixture] public class A_non_empty_list { [Test] public void Is_unchanged_when_head_item_is_readded()  [Test] public void Moves_non_head_item_to_head_when_it_is_readded()  } [TestFixture] public class Any_list { [Test] public void Rejects_addition_of_null_items()  [Test] public void Rejects_indexing_past_its_end()  [Test] public void Rejects_negative_indexing()  } }
Programming with GUTs
So who should you be writing the tests for? For the person trying to understand your code. 
Good tests act as documentation for the code they are testing. They describe how the code works. For each usage scenario, the test(s): 
Describe the context, starting point, or preconditions that must be satisfied 
Illustrate how the software is invoked 
Describe the expected results or postconditions to be verified 
Gerard Meszaros "Write Tests for People"
namespace RecentlyUsedList_spec 
{ 
[TestFixture] 
public class A_new_list ... 
[TestFixture] 
public class An_empty_list 
{ 
[Test] 
public void Retains_a_single_addition( 
[Values("Prague", "Oslo", "Bristol")] string addend) 
{ 
var items = new RecentlyUsedList(); // Given... 
items.Add(addend); // When... 
Assert.AreEqual(1, items.Count); // Then... 
Assert.AreEqual(addend, list[0]); 
} 
[Test] public void Retains_unique_additions_in_stack_order()  
} 
[TestFixture] 
public class A_non_empty_list ... 
[TestFixture] 
public class Any_list ... 
}
Less unit testing dogma. 
More unit testing karma. 
Alberto Savoia 
"The Way of Testivus" 
http://www.artima.com/weblogs/viewpost.jsp?thread=203994

More Related Content

PDF
What We Talk About When We Talk About Unit Testing
PDF
Programming with GUTs
PDF
Functional C++
PDF
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
PDF
Programming with GUTs
PDF
54602399 c-examples-51-to-108-programe-ee01083101
DOCX
informatics practices practical file
PDF
C programs
What We Talk About When We Talk About Unit Testing
Programming with GUTs
Functional C++
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Programming with GUTs
54602399 c-examples-51-to-108-programe-ee01083101
informatics practices practical file
C programs

What's hot (20)

PDF
C program
DOC
Useful c programs
DOCX
Computer Science Practical Science C++ with SQL commands
PDF
Implementing string
DOCX
COMPUTER SCIENCE CLASS 12 PRACTICAL FILE
DOCX
12th CBSE Practical File
PDF
C++ L03-Control Structure
PPT
Arrays
PDF
Array notes
PDF
C++ L02-Conversion+enum+Operators
DOCX
Oops practical file
PPT
An imperative study of c
DOC
Practical Class 12th (c++programs+sql queries and output)
PDF
2014 computer science_question_paper
PPT
Computer Programming- Lecture 6
PPTX
3. chapter ii
PPT
Cquestions
PDF
Stl algorithm-Basic types
PPTX
PPT
FP 201 - Unit 6
C program
Useful c programs
Computer Science Practical Science C++ with SQL commands
Implementing string
COMPUTER SCIENCE CLASS 12 PRACTICAL FILE
12th CBSE Practical File
C++ L03-Control Structure
Arrays
Array notes
C++ L02-Conversion+enum+Operators
Oops practical file
An imperative study of c
Practical Class 12th (c++programs+sql queries and output)
2014 computer science_question_paper
Computer Programming- Lecture 6
3. chapter ii
Cquestions
Stl algorithm-Basic types
FP 201 - Unit 6
Ad

Similar to Programming with GUTs (20)

PDF
2 BytesC++ course_2014_c4_ arrays
PPT
C tutorial
DOCX
Lab. Programs in C
PPT
Computation Chapter 4
PPTX
one dimentional array on programming with C
PDF
VIT351 Software Development VI Unit2
PPTX
C++ Code as Seen by a Hypercritical Reviewer
PPS
pointers 1
PPTX
Python programming workshop session 2
PDF
Developer Experience i TypeScript. Najbardziej ikoniczne duo
DOC
Experiment 06 psiplclannguage expermient.doc
PDF
Some examples of the 64-bit code errors
PPT
C C++ tutorial for beginners- tibacademy.in
PDF
Complete DB code following the instructions Implement the D.pdf
PDF
arraysfor engineering students sdf ppt on arrays
DOCX
R Language
PPTX
COM1407: Arrays
PDF
Programming Fundamentals Arrays and Strings
2 BytesC++ course_2014_c4_ arrays
C tutorial
Lab. Programs in C
Computation Chapter 4
one dimentional array on programming with C
VIT351 Software Development VI Unit2
C++ Code as Seen by a Hypercritical Reviewer
pointers 1
Python programming workshop session 2
Developer Experience i TypeScript. Najbardziej ikoniczne duo
Experiment 06 psiplclannguage expermient.doc
Some examples of the 64-bit code errors
C C++ tutorial for beginners- tibacademy.in
Complete DB code following the instructions Implement the D.pdf
arraysfor engineering students sdf ppt on arrays
R Language
COM1407: Arrays
Programming Fundamentals Arrays and Strings
Ad

More from Kevlin Henney (20)

PDF
Program with GUTs
PDF
The Case for Technical Excellence
PDF
Empirical Development
PDF
Lambda? You Keep Using that Letter
PDF
Lambda? You Keep Using that Letter
PDF
Solid Deconstruction
PDF
Get Kata
PDF
Procedural Programming: It’s Back? It Never Went Away
PDF
Structure and Interpretation of Test Cases
PDF
Agility ≠ Speed
PDF
Refactoring to Immutability
PDF
Old Is the New New
PDF
Turning Development Outside-In
PDF
Giving Code a Good Name
PDF
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
PDF
Thinking Outside the Synchronisation Quadrant
PDF
Code as Risk
PDF
Software Is Details
PDF
Game of Sprints
PDF
Good Code
Program with GUTs
The Case for Technical Excellence
Empirical Development
Lambda? You Keep Using that Letter
Lambda? You Keep Using that Letter
Solid Deconstruction
Get Kata
Procedural Programming: It’s Back? It Never Went Away
Structure and Interpretation of Test Cases
Agility ≠ Speed
Refactoring to Immutability
Old Is the New New
Turning Development Outside-In
Giving Code a Good Name
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Thinking Outside the Synchronisation Quadrant
Code as Risk
Software Is Details
Game of Sprints
Good Code

Recently uploaded (20)

PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
ai tools demonstartion for schools and inter college
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
history of c programming in notes for students .pptx
PDF
System and Network Administration Chapter 2
PPTX
Essential Infomation Tech presentation.pptx
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PPTX
Introduction to Artificial Intelligence
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
top salesforce developer skills in 2025.pdf
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
ai tools demonstartion for schools and inter college
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
history of c programming in notes for students .pptx
System and Network Administration Chapter 2
Essential Infomation Tech presentation.pptx
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Upgrade and Innovation Strategies for SAP ERP Customers
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Softaken Excel to vCard Converter Software.pdf
How Creative Agencies Leverage Project Management Software.pdf
Odoo Companies in India – Driving Business Transformation.pdf
Operating system designcfffgfgggggggvggggggggg
wealthsignaloriginal-com-DS-text-... (1).pdf
Introduction to Artificial Intelligence
Design an Analysis of Algorithms I-SECS-1021-03
top salesforce developer skills in 2025.pdf
CHAPTER 2 - PM Management and IT Context
How to Choose the Right IT Partner for Your Business in Malaysia

Programming with GUTs

  • 1. Programming with GUTs @KevlinHenney kevlin@curbralan.com
  • 3. When you write unit tests, TDD- style or after your development, you scrutinize, you think, and often you prevent problems without even encountering a test failure. Michael Feathers "The Flawed Theory Behind Unit Testing" http://michaelfeathers.typepad.com/michael_feathers_blog/2008/06/the-flawed-theo.html
  • 4. Very many people say "TDD" when they really mean, "I have good unit tests" ("I have GUTs"?). Ron Jeffries tried for years to explain what this was, but we never got a catch-phrase for it, and now TDD is being watered down to mean GUTs. Alistair Cockburn "The modern programming professional has GUTs" http://alistair.cockburn.us/The+modern+programming+professional+has+GUTs
  • 5. size_t ints_to_csv( const int * to_write, size_t how_many, char * output, size_t length);
  • 6. size_t ints_to_csv( const int * to_write, size_t how_many, char * output, size_t length) { size_t result = 0; if(length != 0) { if(how_many == 0) { output[0] = '0'; } else { for(size_t which = 0; which != how_many && result != length; ++which) { result += snprintf( output + result, length - result, which == 0 ? "%i" : ",%i", to_write[which]); } result = result > length - 1 ? length - 1 : result; } } return result; }
  • 7. extern "C" size_t ints_to_csv( const int * to_write, size_t how_many, char * output, size_t length) { size_t result = 0; if(length != 0) { output[length - 1] = '0'; std::ostrstream buffer(output, length - 1); for(size_t which = 0; which != how_many; ++which) buffer << (which == 0 ? "" : ",") << to_write[which]; buffer << std::ends; result = std::strlen(output); } return result; }
  • 9. void test_ints_to_csv() { size_t written = ints_to_csv(NULL, 0, NULL, 0); assert(written == 0); const int input[] = { 42 }; written = ints_to_csv(input, 1, NULL, 0); assert(written == 0); char output[3] = "+++"; written = ints_to_csv(NULL, 0, output, sizeof output); assert(written == 0); assert(output[0] == '0'); memcpy(output, "+++", sizeof output); written = ints_to_csv(input, 1, output, sizeof output); assert(written == 2); assert(strcmp(output, "42") == 0); ... }
  • 10. void test_ints_to_csv() { // no values from null to null output writes nothing size_t written = ints_to_csv(NULL, 0, NULL, 0); assert(written == 0); // value to null output writes nothing const int input[] = { 42 }; written = ints_to_csv(input, 1, NULL, 0); assert(written == 0); // no values to sufficient output writes empty char output[3] = "+++"; written = ints_to_csv(NULL, 0, output, sizeof output); assert(written == 0); assert(output[0] == '0'); // positive value to sufficient output writes value without sign memcpy(output, "+++", sizeof output); written = ints_to_csv(input, 1, output, sizeof output); assert(written == 2); assert(strcmp(output, "42") == 0); ... }
  • 11. void test_ints_to_csv() { // no values from null to null output writes nothing { size_t written = ints_to_csv(NULL, 0, NULL, 0); assert(written == 0); } // value to null output writes nothing { const int input[] = { 42 }; size_t written = ints_to_csv(input, 1, NULL, 0); assert(written == 0); } // no values to sufficient output writes empty { char output[3] = "+++"; size_t written = ints_to_csv(NULL, 0, output, sizeof output); assert(written == 0); assert(output[0] == '0'); } // positive value to sufficient output writes value without sign { const int input[] = { 42 }; char output[3] = "+++"; size_t written = ints_to_csv(input, 1, output, sizeof output); assert(written == 2); assert(strcmp(output, "42") == 0); }
  • 12. void no_values_from_null_to_null_output_writes_nothing() { size_t written = ints_to_csv(NULL, 0, NULL, 0); assert(written == 0); } void value_to_null_output_writes_nothing() { const int input[] = { 42 }; size_t written = ints_to_csv(input, 1, NULL, 0); assert(written == 0); } void no_values_to_sufficient_output_writes_empty() { char output[3] = "+++"; size_t written = ints_to_csv(NULL, 0, output, sizeof output); assert(written == 0); assert(output[0] == '0'); } void positive_value_to_sufficient_output_writes_value_without_sign() { const int input[] = { 42 }; char output[3] = "+++"; size_t written = ints_to_csv(input, 1, output, sizeof output); assert(written == 2); assert(strcmp(output, "42") == 0); } void negative_value_to_sufficient_output_writes_value_with_sign() { const int input[] = { -42 }; char output[4] = "++++"; size_t written = ints_to_csv(input, 1, output, sizeof output); assert(written == 3); assert(strcmp(output, "-42") == 0); } void value_to_insufficient_output_writes_truncated_value() { const int input[] = { 42 }; char output[2] = "++"; size_t written = ints_to_csv(input, 1, output, sizeof output); assert(written == 1); assert(strcmp(output, "4") == 0); } void multiple_values_to_sufficient_output_writes_comma_separated_values() { const int input[] = { 42, -273, 0, 7 }; char output[12] = "++++++++++++"; size_t written = ints_to_csv(input, 4, output, sizeof output); assert(written == 11); assert(strcmp(output, "42,-273,0,7") == 0); } void multiple_values_to_insufficient_output_writes_truncated_value_sequence() { const int input[] = { 42, -273, 0, 7 }; char output[9] = "+++++++++"; size_t written = ints_to_csv(input, 4, output, sizeof output); assert(written == 8); assert(strcmp(output, "42,-273,") == 0); }
  • 13. void no_values_from_null_to_null_output_writes_nothing() { ... } void value_to_null_output_writes_nothing() { ... } void no_values_to_sufficient_output_writes_empty() { ... } void positive_value_to_sufficient_output_writes_value_without_sign() { ... } void negative_value_to_sufficient_output_writes_value_with_sign() { ... } void value_to_insufficient_output_writes_truncated_value() { ... } void multiple_values_to_sufficient_output_writes_comma_separated_values() { ... } void multiple_values_to_insufficient_output_writes_truncated_value_sequence() { ... }
  • 15. size_t ints_to_csv( const int * to_write, size_t how_many, char * output, size_t length);  No values from null to null output writes nothing  Value to null output writes nothing  No values to sufficient output writes empty  Positive value to sufficient output writes value without sign  Negative value to sufficient output writes value with sign  Value to insufficient output writes truncated value  Multiple values to sufficient output writes comma separated values  Multiple values to insufficient output writes truncated value sequence
  • 16. Tests that are not written with their role as specifications in mind can be very confusing to read. The difficulty in understanding what they are testing can greatly reduce the velocity at which a codebase can be changed. Nat Pryce and Steve Freeman "Are Your Tests Really Driving Your Development?"
  • 18. Propositions are vehicles for stating how things are or might be.
  • 19. Thus only indicative sentences which it makes sense to think of as being true or as being false are capable of expressing propositions.
  • 20. public static boolean isLeapYear(int year) ...
  • 28. A test case should be just that: it should correspond to a single case.
  • 29. public class Leap_year_spec { public static class A_year_is_a_leap_year { @Test public void If_it_is_divisible_by_4_but_not_by_100()  @Test public void If_it_is_divisible_by_400()  } public static class A_year_is_not_a_leap_year { @Test public void If_it_is_not_divisible_by_4()  @Test public void If_it_is_divisible_by_100_but_not_by_400()  } }
  • 30. public class Leap_year_spec { public static class A_year_is_a_leap_year { @Test public void If_it_is_divisible_by_4_but_not_by_100()  @Test public void If_it_is_divisible_by_400()  } public static class A_year_is_not_a_leap_year { @Test public void If_it_is_not_divisible_by_4()  @Test public void If_it_is_divisible_by_100_but_not_by_400()  } }
  • 31. method test test test method method
  • 32. public class RecentlyUsedList { ... public RecentlyUsedList() ... public int Count { get... } public string this[int index] { get... } public void Add(string newItem) ... ... }
  • 33. [TestFixture] public class RecentlyUsedListTests { [Test] public void TestConstructor() ... [Test] public void TestCountGet() ... [Test] public void TestIndexerGet() ... [Test] public void TestAdd() ... ... }
  • 34. method test test test method method test test
  • 35. namespace RecentlyUsedList_spec { [TestFixture] public class A_new_list { [Test] public void Is_empty()  } [TestFixture] public class An_empty_list { [Test] public void Retains_a_single_addition()  [Test] public void Retains_unique_additions_in_stack_order()  } [TestFixture] public class A_non_empty_list { [Test] public void Is_unchanged_when_head_item_is_readded()  [Test] public void Moves_non_head_item_to_head_when_it_is_readded()  } [TestFixture] public class Any_list { [Test] public void Rejects_addition_of_null_items()  [Test] public void Rejects_indexing_past_its_end()  [Test] public void Rejects_negative_indexing()  } }
  • 36. namespace RecentlyUsedList_spec { [TestFixture] public class A_new_list { [Test] public void Is_empty()  } [TestFixture] public class An_empty_list { [Test] public void Retains_a_single_addition()  [Test] public void Retains_unique_additions_in_stack_order()  } [TestFixture] public class A_non_empty_list { [Test] public void Is_unchanged_when_head_item_is_readded()  [Test] public void Moves_non_head_item_to_head_when_it_is_readded()  } [TestFixture] public class Any_list { [Test] public void Rejects_addition_of_null_items()  [Test] public void Rejects_indexing_past_its_end()  [Test] public void Rejects_negative_indexing()  } }
  • 38. So who should you be writing the tests for? For the person trying to understand your code. Good tests act as documentation for the code they are testing. They describe how the code works. For each usage scenario, the test(s): Describe the context, starting point, or preconditions that must be satisfied Illustrate how the software is invoked Describe the expected results or postconditions to be verified Gerard Meszaros "Write Tests for People"
  • 39. namespace RecentlyUsedList_spec { [TestFixture] public class A_new_list ... [TestFixture] public class An_empty_list { [Test] public void Retains_a_single_addition( [Values("Prague", "Oslo", "Bristol")] string addend) { var items = new RecentlyUsedList(); // Given... items.Add(addend); // When... Assert.AreEqual(1, items.Count); // Then... Assert.AreEqual(addend, list[0]); } [Test] public void Retains_unique_additions_in_stack_order()  } [TestFixture] public class A_non_empty_list ... [TestFixture] public class Any_list ... }
  • 40. Less unit testing dogma. More unit testing karma. Alberto Savoia "The Way of Testivus" http://www.artima.com/weblogs/viewpost.jsp?thread=203994