Test Driven Development

Why? How? Wow!

Jan KolΓ‘rik

Agenda

  • Introduction
  • Lifecycle
  • Hands-on project
  • Aiming for great code
  • Conclusion

Motivation

Reshaping the conventional cycle

Motivation

But why would we do that?

Motivation

Build confidence and control

Motivation

Gain a new perspective

Motivation

Drive yourself to clean code

Motivation

Take it as a game, challenge yourself

Lifecycle

Designing a pub

Beer

🍺

Bill

πŸ“œ

Client

Waiter

Beer, anyone? 🍺

Requirements:

  • Drinkable (at once)
  • Check it's full

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										interface IBeer 
										{
											bool IsFull();
											void Drink();
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
											}
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												throw new NotImplementedException(); 
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												throw new NotImplementedException(); 
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

⬜ New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												return true;
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

βœ… New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												return true;
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

βœ… New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}

											[Test]
											public void BeerIsEmptyAfterDrinking() {
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												return true;
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

βœ… New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}

											[Test]
											public void BeerIsEmptyAfterDrinking() {
												var beer = new Beer();
												beer.Drink();
												Assert.IsFalse( beer.IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												return true;
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

βœ… New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}

											[Test]
											public void BeerIsEmptyAfterDrinking() {
												var beer = new Beer();
												beer.Drink();
												Assert.IsFalse( beer.IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
											}

											public bool IsFull() {
												return true;
											}

											public void Drink() {
												throw new NotImplementedException();
											}
										}
									
								

Beer, anyone? 🍺

Test list:

βœ… New beer is full

⬜ Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}

											[Test]
											public void BeerIsEmptyAfterDrinking() {
												var beer = new Beer();
												beer.Drink();
												Assert.IsFalse( beer.IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
												this.isFull = true;
											}

											public bool IsFull() {
												return isFull;
											}

											public void Drink() {
												isFull = false;
											}

											private bool isFull;
										}
									
								

Beer, anyone? 🍺

Test list:

βœ… New beer is full

βœ… Drunk beer is empty

									
										[TestFixture]
										class BeerTest
										{
											[Test]
											public void BeerIsFullAfterCreation() {
												Assert.IsTrue( new Beer().IsFull() );
											}

											[Test]
											public void BeerIsEmptyAfterDrinking() {
												var beer = new Beer();
												beer.Drink();
												Assert.IsFalse( beer.IsFull() );
											}
										}
									
								
									
										class Beer : IBeer
										{
											public Beer() {
												this.isFull = true;
											}

											public bool IsFull() {
												return isFull;
											}

											public void Drink() {
												isFull = false;
											}

											private bool isFull;
										}
									
								

Pay the bills πŸ“œ

Requirements:

  • Track beers
  • Pay

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										interface IBill
										{
											void AddBeer();
											void Pay( uint count );
											uint GetUnpaidCount();
										}
									
								

Pay the bills πŸ“œ

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
										}
									
								

Pay the bills πŸ“œ

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, new Bill().GetUnpaidCount() );
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												throw new NotImplementedException();
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												throw new NotImplementedException();
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

⬜ New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return 0;
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return 0;
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return 0;
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return 0;
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return 0;
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												throw new NotImplementedException();
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return 0;
											}
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

⬜ Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												beers = beers + 1;
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return beers;
											}

											private uint beers = 0;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
											}

											public void AddBeer() {
												beers = beers + 1;
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return beers;
											}

											private uint beers = 0;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												throw new NotImplementedException(); 
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

⬜ Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

βœ… Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

βœ… Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayTooManyThrowsAnException() {
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

βœ… Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayTooManyThrowsAnException() {
												Assert.Catch( () => bill.Pay( 1 ) );

												bill.AddBeer();
												Assert.Catch( () => bill.Pay( 2 ) );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

βœ… Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayTooManyThrowsAnException() {
												Assert.Catch( () => bill.Pay( 1 ) );

												bill.AddBeer();
												Assert.Catch( () => bill.Pay( 2 ) );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

βœ… Pay beer

⬜ Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayTooManyThrowsAnException() {
												Assert.Catch( () => bill.Pay( 1 ) );

												bill.AddBeer();
												Assert.Catch( () => bill.Pay( 2 ) );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												if( count > unpaidBeers ) {
													throw new Exception();
												}

												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Pay the bills πŸ“œ

Test list:

βœ… New bill is empty

βœ… Add beer

βœ… Pay beer

βœ… Pay too many

									
										[TestFixture]
										class BillTest
										{
											Bill bill;

											[SetUp]
											public void Init() {
												bill = new Bill();
											}

											[Test]
											public void GetUnpaidCountIsZeroByDefault() {
												Assert.AreEqual( 0, bill.GetUnpaidCount() );
											}

											[Test]
											public void AddBeerIncrementsUnpaidCount() {
												bill.AddBeer();
												Assert.AreEqual( 1, bill.GetUnpaidCount() );
												bill.AddBeer();
												Assert.AreEqual( 2, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayDecreasesUnpaidCount() {
												bill.AddBeer();
												bill.AddBeer();
												bill.AddBeer();

												bill.Pay( 2 );

												Assert.AreEqual( 1, bill.GetUnpaidCount() );
											}

											[Test]
											public void PayTooManyThrowsAnException() {
												Assert.Catch( () => bill.Pay( 1 ) );

												bill.AddBeer();
												Assert.Catch( () => bill.Pay( 2 ) );
											}
										}
									
								
									
										class Bill : IBill
										{
											public Bill() {
												unpaidBeers = 0;
											}

											public void AddBeer() {
												unpaidBeers++;
											}

											public void Pay( uint count ) {
												if( count > unpaidBeers ) {
													throw new Exception();
												}

												unpaidBeers -= count;
											}

											public uint GetUnpaidCount() {
												return unpaidBeers;
											}

											private uint unpaidBeers;
										}
									
								

Customer is always right

Requirements:

  • Order beer
  • Keep bill
  • Pay bill
									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client() {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client() {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
											}

											public void OrderBeer() {
												throw new NotImplementedException(); 
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}

											private IWaiter waiter;
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
												this.waiter = waiter;
											}

											public void OrderBeer() {
												waiter.Order( this );
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}

											private IWaiter waiter;
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
												this.waiter = waiter;
											}

											public void OrderBeer() {
												waiter.Order( this );
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}

											private IWaiter waiter;
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
												this.waiter = waiter;
											}

											public void OrderBeer() {
												waiter.Order( this );
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}

											private IWaiter waiter;
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
												this.waiter = waiter;
											}

											public void OrderBeer() {
												waiter.Order( this );
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												throw new NotImplementedException();
											}

											private IWaiter waiter;
										}
									
								

Customer is always right

									
										interface IClient
										{
											void OrderBeer();
											void ReceiveBeer( IBeer beer );
											void SetBill( IBill bill );
											IBill GetBill();
											void PayBill();
										}

										interface IWaiter
										{
											void Order( IClient client );
											void Pay( IClient client );
										}
									
								
									
										
									
								
									
										class Client : IClient
										{
											public Client( IWaiter waiter ) {
												this.waiter = waiter;
											}

											public void OrderBeer() {
												waiter.Order( this );
											}

											public void SetBill( IBill bill ) {
												throw new NotImplementedException();
											}

											public IBill GetBill() {
												throw new NotImplementedException();
											}

											public void PayBill() {
												throw new NotImplementedException();
											}

											public void ReceiveBeer( IBeer beer ) {
												beer.Drink();
											}

											private IWaiter waiter;
										}
									
								

Customer is always right

The rest is left as an exercise ...

Let's write great code

... by just using TDD?

SOLID code

Single responsibility

A module should be responsible to one, and only one, actor.

SOLID code

Single responsibility

							
								interface IReportProcessor 
								{
									IReport Parse( string text );
									void Print( IReport report );
								}
							
						

SOLID code

Single responsibility

							
								interface IReportParser 
								{
									IReport Parse( string text );
								}

								interface IReportPrinter 
								{
									void Print( IReport report );
								}
							
						

SOLID code

Open-closed principle

Entities should be open for extension, but closed for modification.

SOLID code

Open-closed principle

SOLID code

Open-closed principle

SOLID code

Liskov substitution

Subclasses should be interchangeable with their base classes seamlessly.

SOLID code

Liskov substitution

									
										Apple apple = new Orange();
										apple.GetColor();
									
								

SOLID code

Liskov substitution

									
										Fruit fruit = new Orange();
										fruit.GetColor();
									
								

SOLID code

Interface segregation

Clients should not be forced to depend on interfaces they do not use.

SOLID code

Interface segregation

									
										interface ILaundry
										{
											void Wash();
											void Dry();
										}
									
								
									
										class ComboWashingMashine : ILaundry
										{
											public void Wash() {
												// do the work
											}
											public void Dry() {
												// do the work
											}
										}
										class SimpleWashingMashine : ILaundry
										{
											public void Wash() {
												// do the work as well
											}
											public void Dry() {
												// whoops, can't do
												throw new NotImplementedException();
											}
										}
									
								

SOLID code

Interface segregation

									
										interface IWashLaundry
										{
											void Wash();
										}

										interface IDryLaundry
										{
											void Dry();
										}
									
								
									
										class ComboWashingMashine : IWashLaundry, 
										                            IDryLaundry
										{
											public void Wash() {
												// do the work
											}
											public void Dry() {
												// do the work
											}
										}
										class SimpleWashingMashine : IWashLaundry
										{
											public void Wash() {
												// do the work
											}
										}
									
								

SOLID code

Dependency inversion

One should depend upon abstractions, not concretions.

SOLID code

Dependency inversion

SOLID code

Dependency inversion

Application

Device driver

Device driver

Device driver

  • Simplified class diagram
  • Business logic
  • API parts

Device driver: Sending command

Device driver: Service intervention

Device driver: Reading values

Device driver: State change

Device driver: Summary

  • Written in C++
  • 3 projects: API, BL, Tests
  • Complete BL coverage by TDD
  • Almost 100 abstract classes
  • More than 1300 test cases

Benefits

  • Complete code coverage
  • Living documentation
  • Modular and extensible design
  • Early bug detection
  • Programmer confidence

References

  • Kent, B. (2002). Test Driven Development: By Example. Addison-Wesley Professional.
  • Martin, R. C. (2002). Agile Software Development, Principles, Patterns, and Practices. Pearson.
  • NUnit, Moq frameworks
  • reveal.js presentations