Writing Good Tests

170
WRITING GOOD TESTS @matteobaglini #IAD14

description

Se guardiamo oltre la meccanica, il TDD è una tecnica complessa perché richiede molteplici skill. Da principiante dopo l’implementazione di poche storie ti imbatti nel problema dei test che si rompono ad ogni refactoring, è arrivato il momento di migliorare i propri skill di scrittura dei test. Nel talk analizzeremo la struttura dei test, quali sono le bad smell più comuni e come porvi rimedio.

Transcript of Writing Good Tests

Page 1: Writing Good Tests

WRITING

GOOD

TESTS

@matteobaglini #IAD14

Page 2: Writing Good Tests

TEST-DRIVEN DEVELOPMENT

Page 3: Writing Good Tests

@iacoware

Page 4: Writing Good Tests

TDD MECHANICS

Page 5: Writing Good Tests

TDD IS A MULTIFACETED

SKILL

Page 6: Writing Good Tests

SKILL ON

CLEAN

CODE

Page 7: Writing Good Tests

SKILL ON

REFACTORING

SAFELY

Page 8: Writing Good Tests

SKILL ON

DISCOVER

TEST CASES

Page 9: Writing Good Tests

SKILL ON

CHOOSE

NEXT TESTS

Page 10: Writing Good Tests

SKILL ON

WRITING

TESTS

Page 11: Writing Good Tests

SKILL ON

WRITING GOOD

TESTS

Page 12: Writing Good Tests

FOCUS ON

WRITING GOOD

TESTS

Page 13: Writing Good Tests

PROPERTIES

Page 14: Writing Good Tests

TESTS MUST BE

ISOLATED

Page 15: Writing Good Tests

TESTS MUST BE

INDIPENDENT

Page 16: Writing Good Tests

TESTS MUST BE

DETERMINISTIC

Page 17: Writing Good Tests

AFFECT

EXECUTION

Page 18: Writing Good Tests

WHAT ABOUT

TEST CODE?

Page 19: Writing Good Tests

BAD

CODE

SMELLS

Page 20: Writing Good Tests

BAD

TEST CODE

SMELLS

Page 21: Writing Good Tests

LEAD TO

FRAGILITYON REFACTORING

Page 22: Writing Good Tests

WHY SHOULD

WE CARE?

Page 23: Writing Good Tests

WHAT‘S AGILE PRACTICES’

ULTIMATE GOAL?

Page 24: Writing Good Tests

REDUCE

THE COST

AND RISK

OF CHANGE

Page 25: Writing Good Tests

FRAGILE TESTS

INCREASE

COSTS

Page 26: Writing Good Tests

FRAGILE TESTS

LOSS OF

CONFIDENCE

Page 27: Writing Good Tests

FRAGILE TESTS

REDUCE

SUSTAINABILITY

Page 28: Writing Good Tests

PAY OFF

YOUR

TECHNICAL DEBT

Page 29: Writing Good Tests

WHERE ISCOMPLEXITY?

Page 30: Writing Good Tests

[Fact]

public void ScenarioUnderTest()

{

// Arrange

// Act

// Assert

}

Page 31: Writing Good Tests

START

FROM

ASSERT

Page 32: Writing Good Tests

01

COMPARING

OBJECTS

Page 33: Writing Good Tests

[Fact]

public void Subtract()

{

var one = TimeRange.Parse("09:00-10:00");

var two = TimeRange.Parse("09:15-09:45");

var result = one.Subtract(two);

Assert.Equal(2, result.Length);

Assert.Equal(TimeSpan.Parse("09:00"), result[0].Begin);

Assert.Equal(TimeSpan.Parse("09:15"), result[0].End);

Assert.Equal(TimeSpan.Parse("09:45"), result[1].Begin);

Assert.Equal(TimeSpan.Parse("10:00"), result[1].End);

}

Page 34: Writing Good Tests

[Fact]

public void Subtract()

{

var one = TimeRange.Parse("09:00-10:00");

var two = TimeRange.Parse("09:15-09:45");

var result = one.Subtract(two);

Assert.Equal(2, result.Length);

Assert.Equal(TimeSpan.Parse("09:00"), result[0].Begin);

Assert.Equal(TimeSpan.Parse("09:15"), result[0].End);

Assert.Equal(TimeSpan.Parse("09:45"), result[1].Begin);

Assert.Equal(TimeSpan.Parse("10:00"), result[1].End);

}

Page 35: Writing Good Tests

WHICH KIND

OF OBJECTS?

Page 36: Writing Good Tests

VALUE OBJECTS

Page 37: Writing Good Tests

VALUE OBJECTS

MODEL FIXED

QUANTITIES

Page 38: Writing Good Tests

VALUE OBJECTS

IMMUTABLE

Page 39: Writing Good Tests

VALUE OBJECTS

NO IDENTITY

Page 40: Writing Good Tests

VALUE OBJECTS

EQUAL IF THEY

HAVE SAME STATE

Page 41: Writing Good Tests

[Fact]

public void Subtract()

{

var one = TimeRange.Parse("09:00-10:00");

var two = TimeRange.Parse("09:15-09:45");

var result = one.Subtract(two);

Assert.Equal(2, result.Length);

Assert.Equal(TimeSpan.Parse("09:00"), result[0].Begin);

Assert.Equal(TimeSpan.Parse("09:15"), result[0].End);

Assert.Equal(TimeSpan.Parse("09:45"), result[1].Begin);

Assert.Equal(TimeSpan.Parse("10:00"), result[1].End);

}

Page 42: Writing Good Tests

[Fact]

public void Equality()

{

var slot = TimeRange.Parse("09:00-10:00");

var same = TimeRange.Parse("09:00-10:00");

var differentBegin = TimeRange.Parse("08:00-10:00");

var differentEnd = TimeRange.Parse("09:00-13:00");

Assert.Equal(slot, same);

Assert.NotEqual(slot, differentBegin);

Assert.NotEqual(slot, differentEnd);

}

Page 43: Writing Good Tests

[Fact]

public void Subtract()

{

var one = TimeRange.Parse("09:00-10:00");

var two = TimeRange.Parse("09:15-09:45");

var result = one.Subtract(two);

Assert.Equal(2, result.Length);

Assert.Equal(TimeSpan.Parse("09:00"), result[0].Begin);

Assert.Equal(TimeSpan.Parse("09:15"), result[0].End);

Assert.Equal(TimeSpan.Parse("09:45"), result[1].Begin);

Assert.Equal(TimeSpan.Parse("10:00"), result[1].End);

}

Page 44: Writing Good Tests

[Fact]

public void Subtract()

{

var one = TimeRange.Parse("09:00-10:00");

var two = TimeRange.Parse("09:15-09:45");

var result = one.Subtract(two);

Assert.Contains(TimeRange.Parse("09:00-09:15"), result);

Assert.Contains(TimeRange.Parse("09:45-10:00"), result);

}

Page 45: Writing Good Tests

OBJECTS

Page 46: Writing Good Tests

OBJECTS

MODEL STATE

AND PROCESSING OVER TIME

Page 47: Writing Good Tests

OBJECTS

MUTABLE

Page 48: Writing Good Tests

OBJECTS

HAS IDENTITY

Page 49: Writing Good Tests

OBJECTS

EQUAL IF THEY

HAVE SAME

IDENTITY

Page 50: Writing Good Tests

[Fact]

public void GetAllMessages()

{

var messageId1 = Guid.NewGuid();

var messageId2 = Guid.NewGuid();

var store = new MessageStore();

store.Add(DummyMessage(messageId1));

store.Add(DummyMessage(messageId2));

var result = store.GetAll();

Assert.Equal(2, result.Count);

Assert.NotNull(result.Single(x => x.Id == messageId1));

Assert.NotNull(result.Single(x => x.Id == messageId2));

}

Page 51: Writing Good Tests

[Fact]

public void GetAllMessages()

{

var messageId1 = Guid.NewGuid();

var messageId2 = Guid.NewGuid();

var store = new MessageStore();

store.Add(DummyMessage(messageId1));

store.Add(DummyMessage(messageId2));

var result = store.GetAll();

Assert.Equal(2, result.Count);

Assert.NotNull(result.Single(x => x.Id == messageId1));

Assert.NotNull(result.Single(x => x.Id == messageId2));

}

Page 52: Writing Good Tests

[Fact]

public void Equality()

{

var message = DummyMessage(Guid.NewGuid());

var same = DummyMessage(message.Id);

var diffent = DummyMessage(Guid.NewGuid());

Assert.Equal(message, same);

Assert.NotEqual(message, diffent);

}

Page 53: Writing Good Tests

[Fact]

public void GetAllMessages()

{

var messageId1 = Guid.NewGuid();

var messageId2 = Guid.NewGuid();

var store = new MessageStore();

store.Add(DummyMessage(messageId1));

store.Add(DummyMessage(messageId2));

var result = store.GetAll();

Assert.Equal(2, result.Count);

Assert.NotNull(result.Single(x => x.Id == messageId1));

Assert.NotNull(result.Single(x => x.Id == messageId2));

}

Page 54: Writing Good Tests

[Fact]

public void GetAllMessages()

{

var messageId1 = Guid.NewGuid();

var messageId2 = Guid.NewGuid();

var store = new MessageStore();

store.Add(DummyMessage(messageId1));

store.Add(DummyMessage(messageId2));

var result = store.GetAll();

Assert.Contains(DummyMessage(messageId1), result);

Assert.Contains(DummyMessage(messageId2), result);

}

Page 55: Writing Good Tests

WHEN YOU

CAN’T CHANGETHE OBJECT?

Page 56: Writing Good Tests

[Fact]

public void GetContactsDetails()

{

var controller = new ContactsController();

var result = controller.Details(Guid.NewGuid());

var viewResult = Assert.IsType<ViewResult>(result);

Assert.Equal(String.Empty, viewResult.ViewName);

Assert.IsAssignableFrom<ContactModel>(viewResult.Model);

}

Page 57: Writing Good Tests

[Fact]

public void GetContactsDetails()

{

var controller = new ContactsController();

var result = controller.Details(Guid.NewGuid());

var viewResult = Assert.IsType<ViewResult>(result);

Assert.Equal(String.Empty, viewResult.ViewName);

Assert.IsAssignableFrom<ContactModel>(viewResult.Model);

}

Page 58: Writing Good Tests

[Fact]

public void GetContactsDetails()

{

var controller = new ContactsController();

var result = controller.Details(Guid.NewGuid());

MvcAssert.IsViewResult<ContactModel>(result);

}

Page 59: Writing Good Tests

02

TESTING ONLY

ONE CONCERN

Page 60: Writing Good Tests

[Fact]

public void SingleElement()

{

var input = "<element />";

var tag = Tag.Parse(input);

Assert.True(tag.IsEmpty());

Assert.Equal("element", tag.Name());

Assert.Empty(tag.ChildNodes());

}

Page 61: Writing Good Tests

[Fact]

public void SingleElement()

{

var input = "<element />";

var tag = Tag.Parse(input);

Assert.True(tag.IsEmpty());

Assert.Equal("element", tag.Name());

Assert.Empty(tag.ChildNodes());

}

Page 62: Writing Good Tests

TEST CAN

FAIL FOR

TOO MANY

REASONS

Page 63: Writing Good Tests

[Fact]

public void SingleElement()

{

var input = "<element />";

var tag = Tag.Parse(input);

Assert.True(tag.IsEmpty());

Assert.Equal("element", tag.Name());

Assert.Empty(tag.ChildNodes());

}

Page 64: Writing Good Tests

[Fact]

public void SingleElementNoContent()

{

var input = "<element />";

var tag = Tag.Parse(input);

Assert.True(tag.IsEmpty());

}

Page 65: Writing Good Tests

[Fact]

public void SingleElementName()

{

var input = "<element />";

var tag = Tag.Parse(input);

Assert.Equal("element", tag.Name());

}

Page 66: Writing Good Tests

[Fact]

public void SingleElementNoChildren()

{

var input = "<element />";

var tag = Tag.Parse(input);

Assert.Empty(tag.ChildNodes());

}

Page 67: Writing Good Tests

03

TESTING

BEHAVIOUR

Page 68: Writing Good Tests

STATEVS

BEHAVIOUR

Page 69: Writing Good Tests

FOCUS ON

TESTING STATE

Page 70: Writing Good Tests

[Fact]

public void NewStackIsEmpty()

{

var stack = new Stack();

Assert.True(stack.IsEmpty());

}

Page 71: Writing Good Tests

[Fact]

public void StackIsNotEmptyAfterAddItem()

{

var stack = new Stack();

stack.Push(5);

Assert.False(stack.IsEmpty());

}

Page 72: Writing Good Tests

[Fact]

public void StackCountIncreaseAfterAddItems()

{

var items = new[] { 1, 2, 3 };

var stack = new Stack();

stack.Push(items[0]);

stack.Push(items[1]);

stack.Push(items[2]);

Assert.Equal(items.Length, stack.Count());

}

Page 73: Writing Good Tests

FOCUS ON

TESTING

BEHAVIOUR

Page 74: Writing Good Tests

[Fact]

public void PopOneItem()

{

var item = 5;

var stack = new Stack();

stack.Push(item);

Assert.Equal(item, stack.Pop());

}

Page 75: Writing Good Tests

[Fact]

public void PopManyItems()

{

var items = new[] { 1, 2, 3 };

var stack = new Stack();

stack.Push(items[0]);

stack.Push(items[1]);

stack.Push(items[2]);

Assert.Equal(items.Reverse(),

new[] { stack.Pop(), stack.Pop(), stack.Pop() });

}

Page 76: Writing Good Tests

[Fact]

public void PopEmptyStack()

{

var stack = new Stack();

Assert

.Throws<InvalidOperationException>(() => stack.Pop());

}

Page 77: Writing Good Tests

04

AVOID TESTING

PRIVATE METHODS

Page 78: Writing Good Tests

public class Invoice

{

// ...

public Eur Total()

{

var gross = ComputeGrossAmount(/* args */);

var tax = ComputeTaxAmount(/* args */);

return gross.Add(tax);

}

}

Page 79: Writing Good Tests

public class Invoice

{

// ...

Eur ComputeGrossAmount(/* args */)

{

/* many lines of complex

* and commented code */

}

}

Page 80: Writing Good Tests

public class Invoice

{

// ...

Eur ComputeTaxAmount(/* args */)

{

/* many lines of complex

* and commented code */

}

}

Page 81: Writing Good Tests

[Fact]

public void GrossAmountCalculation()

{

// We want invoke private ComputeGrossAmount method

}

[Fact]

public void TaxAmountCalculation()

{

// We want invoke private ComputeTaxAmount method

}

Page 82: Writing Good Tests

DON’T DO IT!

Page 83: Writing Good Tests

DON’T DO IT!

Page 84: Writing Good Tests

DON’T DO IT!

Page 85: Writing Good Tests

CONSIDER APPLYING

METHOD

OBJECT

Page 86: Writing Good Tests

MAKE AN

OBJECT

OUT OF THE METHOD

Page 87: Writing Good Tests

public class Invoice

{

// ...

public Eur Total()

{

var gross = new GrossAmount(/* args */).Compute();

var tax = new TaxAmount(/* args */).Compute();

return gross.Add(tax);

}

}

Page 88: Writing Good Tests

NOW TESTEXTRACTED

OBJECTS

Page 89: Writing Good Tests

05

SAME LEVEL

OF ABSTRACTION

Page 90: Writing Good Tests

[Fact]

public void SetCommand()

{

var cache = new Cache();

var dispatcher = new CommandDispatcher(cache);

dispatcher.Process("SET foo bar");

var actual = cache.Get("foo");

Assert.Equal("bar", actual);

}

Page 91: Writing Good Tests

[Fact]

public void SetCommand()

{

var cache = new Cache();

var dispatcher = new CommandDispatcher(cache);

dispatcher.Process("SET foo bar");

var actual = cache.Get("foo");

Assert.Equal("bar", actual);

}

Page 92: Writing Good Tests

ACT AT HIGHER

ASSERT AT LOWER

Page 93: Writing Good Tests

REMEMBER DIP?

Page 94: Writing Good Tests

DON’T CROSS

THE STREAM

Page 95: Writing Good Tests

[Fact]

public void SetCommand()

{

var cache = new Cache();

var dispatcher = new CommandDispatcher(cache);

dispatcher.Process("SET foo bar");

var actual = cache.Get("foo");

Assert.Equal("bar", actual);

}

Page 96: Writing Good Tests

[Fact]

public void SetCommand()

{

var cache = new Cache();

var dispatcher = new CommandDispatcher(cache);

dispatcher.Process("SET foo bar");

var actual = dispatcher.Process("GET foo");

Assert.Equal("bar", actual);

}

Page 97: Writing Good Tests

WHEN IT

ISN’T YOURRESPONSIBILITY?

Page 98: Writing Good Tests

[Fact]

public void SetCommand()

{

var log = new TransactionLog();

var cache = new PersistentCache(log);

cache.Set("foo", "bar");

var written = log.History().Take(1).Single();

Assert.Equal("SET foo bar\r\n", written);

}

Page 99: Writing Good Tests

[Fact]

public void SetCommand()

{

var log = new TransactionLog();

var cache = new PersistentCache(log);

cache.Set("foo", "bar");

var written = log.History().Take(1).Single();

Assert.Equal("SET foo bar\r\n", written);

}

Page 100: Writing Good Tests

PURE

FABRICATION

Page 101: Writing Good Tests

[Fact]

public void SetCommand()

{

var log = new TransactionLog();

var cache = new PersistentCache(log);

cache.Set("foo", "bar");

var written = log.History().Take(1).Single();

Assert.Equal("SET foo bar\r\n", written);

}

Page 102: Writing Good Tests

[Fact]

public void SetCommand()

{

var log = new TransactionLog();

var cache = new PersistentCache(log);

cache.Set("foo", "bar");

var written = log.History().Take(1).Single();

Assert.Equal("SET foo bar\r\n", written);

}

Page 103: Writing Good Tests

[Fact]

public void SetCommand()

{

var log = new TransactionLog();

var cache = new PersistentCache(log);

var monitor = new Monitor(log);

cache.Set("foo", "bar");

var written = log.History().Take(1).Single();

Assert.Equal("SET foo bar\r\n", written);

}

Page 104: Writing Good Tests

[Fact]

public void SetCommand()

{

var log = new TransactionLog();

var cache = new PersistentCache(log);

var monitor = new Monitor(log);

cache.Set("foo", "bar");

var written = monitor.LastLog();

Assert.Equal("SET foo bar\r\n", written);

}

Page 105: Writing Good Tests

WHEN

SIDE EFFECTS

AREN’T DIRECTLYRELATED?

Page 106: Writing Good Tests

[Fact]

public void CommandStatistics()

{

var channel = new StatisticsChannel();

var cache = new Cache(channel);

var dashboard = new RealTimeDashboard(channel);

cache.Set("foo", "bar");

var received = dashboard.LastReceived();

Assert.Equal("NAME: SET; ARGS: foo bar", received);

}

Page 107: Writing Good Tests

[Fact]

public void CommandStatistics()

{

var channel = new StatisticsChannel();

var cache = new Cache(channel);

var dashboard = new RealTimeDashboard(channel);

cache.Set("foo", "bar");

var received = dashboard.LastReceived();

Assert.Equal("NAME: SET; ARGS: foo bar", received);

}

Page 108: Writing Good Tests

BREAK

THE FLOW

Page 109: Writing Good Tests

OBSERVER

PLUS

SELF SHUNT

Page 110: Writing Good Tests

public interface ICacheSubscriber

{

void NotifyCommandExecuted(string name, string args);

}

Page 111: Writing Good Tests

public class CacheTests : ICacheSubscriber

{

string ExecutedCommandName;

string ExecutedCommandArgs;

public void NotifyCommandExecuted(string name,

string args)

{

ExecutedCommandName = name;

ExecutedCommandArgs = args;

}

// ...

}

Page 112: Writing Good Tests

[Fact]

public void CommandStatistics()

{

var channel = new StatisticsChannel();

var cache = new Cache(channel);

var dashboard = new RealTimeDashboard(channel);

cache.Set("foo", "bar");

var received = dashboard.LastReceived();

Assert.Equal("NAME: SET; ARGS: foo bar", received);

}

Page 113: Writing Good Tests

[Fact]

public void CommandStatistics()

{

var cache = new Cache(channel);

cache.Set("foo", "bar");

Assert.Equal("NAME: SET; ARGS: foo bar", received);

}

Page 114: Writing Good Tests

[Fact]

public void CommandStatistics()

{

var cache = new Cache(channel);

cache.Set("foo", "bar");

Assert.Equal("NAME: SET; ARGS: foo bar", received);

}

Page 115: Writing Good Tests

[Fact]

public void CommandStatistics()

{

ICacheSubscriber subscriber = this;

var cache = new Cache(subscriber);

cache.Set("foo", "bar");

Assert.Equal("NAME: SET; ARGS: foo bar", received);

}

Page 116: Writing Good Tests

[Fact]

public void NotifyCommandExecution()

{

ICacheSubscriber subscriber = this;

var cache = new Cache(subscriber);

cache.Set("foo", "bar");

Assert.Equal("SET", this.ExecutedCommandName);

Assert.Equal("foo bar", this.ExecutedCommandArgs);

}

Page 117: Writing Good Tests

06

RESPECT

ABSTRACTION

Page 118: Writing Good Tests

[Fact]

public void Overcrowding()

{

var moreThanThreeNeighbors = 6;

var cell = new Cell(alive: true);

var alive = cell.StayAlive(moreThanThreeNeighbors);

Assert.False(alive);

}

Page 119: Writing Good Tests

[Fact]

public void Overcrowding()

{

var moreThanThreeNeighbors = 6;

var cell = new Cell(alive: true);

var alive = cell.StayAlive(moreThanThreeNeighbors);

Assert.False(alive);

}

Page 120: Writing Good Tests

SEGREGATE

DECISIONS

Page 121: Writing Good Tests

[Fact]

public void Overcrowding()

{

var moreThanThreeNeighbors = 6;

var cell = new Cell(alive: true);

var alive = cell.StayAlive(moreThanThreeNeighbors);

Assert.False(alive);

}

Page 122: Writing Good Tests

[Fact]

public void Overcrowding()

{

var moreThanThreeNeighbors = 6;

var cell = Cell.Live();

var alive = cell.StayAlive(moreThanThreeNeighbors);

Assert.False(alive);

}

Page 123: Writing Good Tests

INFORMATION

HIDING

Page 124: Writing Good Tests

07

COMPLEX

OBJECT

CREATION

Page 125: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new Order(Location.TakeAway,

new OrderLines(

new OrderLine("Latte",

new Quantity(1),

Size.Small,

new Pounds(1, 50)),

new OrderLine("Cappuccino",

new Quantity(2),

Size.Large,

new Pounds(2, 50))));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 126: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new Order(Location.TakeAway,

new OrderLines(

new OrderLine("Latte",

new Quantity(1),

Size.Small,

new Pounds(1, 50)),

new OrderLine("Cappuccino",

new Quantity(2),

Size.Large,

new Pounds(2, 50))));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 127: Writing Good Tests

EXTRACT

FACTORY METHOD

Page 128: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = CreateOrderWithLatteAndCappuccino();

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 129: Writing Good Tests

OBJECT

MOTHER

Page 130: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = TestOrders

.CreateOrderWithLatteAndCappuccino();

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 131: Writing Good Tests

Order CreateEmptyOrder() { /* ... */ }

Page 132: Writing Good Tests

Order CreateEmptyOrder() { /* ... */ }

Order CreateOrderWithLatteAndCappuccino() { /* ... */ }

Page 133: Writing Good Tests

Order CreateEmptyOrder() { /* ... */ }

Order CreateOrderWithLatteAndCappuccino() { /* ... */ }

Order CreateOrderWithLatteMultipleQuantities() { /* ... */ }

Page 134: Writing Good Tests

Order CreateEmptyOrder() { /* ... */ }

Order CreateOrderWithLatteAndCappuccino() { /* ... */ }

Order CreateOrderWithLatteMultipleQuantities() { /* ... */ }

Order CreateOrderWithCaffeSpecialDiscount() { /* ... */ }

Page 135: Writing Good Tests

POOR NAME

Page 136: Writing Good Tests

LOSS OF CONTEXT

Page 137: Writing Good Tests

DOESN’T SCALE

Page 138: Writing Good Tests

COMMON

SHARED FIXTURE

Page 139: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = CreateCommonOrder();

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 140: Writing Good Tests

[Fact]

public void DiscountedOrderCost()

{

var order = CreateCommonOrder();

order.Add(Discounts.ByQuantity(2));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 141: Writing Good Tests

[Fact]

public void DiscountedOrderCost()

{

var order = CreateCommonOrder();

order.Add(Discounts.ByQuantity(2));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 142: Writing Good Tests

SAME FAILURE

Page 143: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new Order(Location.TakeAway,

new OrderLines(

new OrderLine("Latte",

new Quantity(1),

Size.Small,

new Pounds(1, 50)),

new OrderLine("Cappuccino",

new Quantity(2),

Size.Large,

new Pounds(2, 50))));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 144: Writing Good Tests

DECOUPLE

WHAT IS NEEDED

Page 145: Writing Good Tests

FROM HOW

IT IS STRUCTURED

Page 146: Writing Good Tests

BUILDER

PATTERN

Page 147: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new Order(Location.TakeAway,

new OrderLines(

new OrderLine("Latte",

new Quantity(1),

Size.Small,

new Pounds(1, 50)),

new OrderLine("Cappuccino",

new Quantity(2),

Size.Large,

new Pounds(2, 50))));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 148: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new OrderBuilder()

.WithLocation(Location.TakeAway)

.WithLine(new OrderLineBuilder()

.WithName("Latte")

.WithPrice(new Pounds(1, 50)))

.WithLine(new OrderLineBuilder()

.WithName("Cappuccino")

.WithQuantity(new Quantity(2))

.WithSize(Size.Large)

.WithPrice(new Pounds(1, 50)));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 149: Writing Good Tests

STILL

EXPOSE

STRUCTURE

Page 150: Writing Good Tests

INTRODUCE

HIGHER LEVEL

METHODS

Page 151: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new OrderBuilder()

.WithLocation(Location.TakeAway)

.WithLine(new OrderLineBuilder()

.WithName("Latte")

.WithPrice(new Pounds(1, 50)))

.WithLine(new OrderLineBuilder()

.WithName("Cappuccino")

.WithQuantity(new Quantity(2))

.WithSize(Size.Large)

.WithPrice(new Pounds(1, 50)));

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 152: Writing Good Tests

[Fact]

public void OrderCost()

{

var order = new OrderBuilder()

.TakeAway()

.WithOne("Latte", "1,50")

.WithTwoLarge("Cappuccino", "1,50");

var result = order.Cost();

Assert.Equal(new Pounds(6, 50), result);

}

Page 153: Writing Good Tests

DOMAIN

SPECIFIC

LANGUAGE

Page 154: Writing Good Tests

RECAP

Page 155: Writing Good Tests

STOP THE MADNESS

Page 156: Writing Good Tests

TEST CODE

NEED LOVE

Page 157: Writing Good Tests

LISTEN TO

TEST CODE

Page 158: Writing Good Tests

TOO

MUCH

INTIMACY

Page 159: Writing Good Tests

EXPRESS INTENT

Page 160: Writing Good Tests

FOCUS ON

WHAT

Page 161: Writing Good Tests

NOT ON

HOW

Page 162: Writing Good Tests

REMOVE DUPLICATION

Page 163: Writing Good Tests

INTRODUCE ABSTRACTIONS

Page 164: Writing Good Tests

RESPECT ABSTRACTIONLEVELS

Page 165: Writing Good Tests

EVOLVE TESTINFRASTRUCTURE

Page 166: Writing Good Tests

BABY STEPS

Page 167: Writing Good Tests

CONTINUOUSREFACTORING

Page 168: Writing Good Tests

IMPROVE

YOUR

SKILLS

Page 169: Writing Good Tests

MATTEO BAGLINI

FREELANCE

SOFTWARE DEVELOPER

TECHNICAL COACH

@matteobaglini

Page 170: Writing Good Tests

https://joind.in/talk/view/12770