unit test craftsmanship-finalxunitpatterns.com/~gerard/agiletourvietnam2016.pdf · (pehgghg 7hohfrp...

78
Unit Test Craftsmanship Gerard Meszaros Independent Consultant 1 Copyright 2016 Gerard Meszaros http://vietnam2016.xunitpatterns.com Independent Consultant CTO of FeedXL.Com [email protected] @gerardmes #xunitpatterns These Slides: http://vietnam2016.xunitpatterns.com

Upload: others

Post on 13-Oct-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Unit Test CraftsmanshipGerard Meszaros

Independent Consultant

1 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Independent Consultant

CTO of FeedXL.Com

[email protected]

@gerardmes

#xunitpatterns

These Slides: http://vietnam2016.xunitpatterns.com

Page 2: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

EmbeddedTelecom

My Background

•Software developer

•Development manager

•Project Manager

•Software architect

2 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Product & I.T.

I.T.

Gerard [email protected]

Software architect

•OOA/OOD Mentor

•Requirements (Use Case) Mentor

•XP/TDD Mentor

•Agile PM Mentor

•Test Automation Consultant & Trainer

•Lean/Agile Coach/Consultant

Page 3: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Audience Survey

3 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.comhttps://lmsa.site-ym.com/news/221726/Sex-and-Gender-Medical-Education-National-Student-Survey.htm

Page 4: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

What Does it Take To be Successful?

Programming Experience

+ XUnit Experience

+ Testing experience

-------------------------------

4 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

-------------------------------

Robust Automated Tests

Page 5: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

A Sobering Thought

Expect to have just as much test code as production code!

5 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

The Challenge: How To Prevent Doubling Cost of Software Maintenance?

Page 6: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Coding Objectives Comparison

CrucialImportantMaintainability

SomewhatCrucialExecution Speed

NotImportantReusability

CrucialImportantCorrectness

TestwareProduction

6 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

NotImportantReusability

NotImportantFlexibility

CrucialImportant?Simplicity

CrucialImportant?Ease of writing

CrucialNot?Obviousness

Page 7: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Why are They so Crucial?

• Tests need to be maintained along with rest of the software.

• Expect there to be as much testware as software.

• Testware must be much easier to maintain than the software, otherwise:

7 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

than the software, otherwise:– It will slow you down

– It will get left behind

– Value drops to zero

– You’ll go back to manual testing

Critical Success Factor:

Writing tests in a maintainable style

Page 8: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

TestAutomation

Economics of Maintainability

Automated unit testing/checking is a lot easier to sell on

• Cost reduction than

• Software Quality Improvement or

• Quality of Life Improvement

Cost of Test Automation + Ongoing

Maintenance

8 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

AutomationEffort

After Automation

DevelopmentEffort

saved effort

time

Increasedeffort(Hump) Ongoing

effortInitialeffort

Page 9: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

TestAutomation

Economics of Maintainability

Test Automation is a lot easier to sell on

• Cost reduction than

• Software Quality Improvement or

• Quality of Life Improvement

Cost of Test Automation + Ongoing

Maintenance

9 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

AutomationEffort

saved effort

time

Initialeffort

Ongoingeffort

Increasedeffort(Hump)

Unsustainable Automation

DevelopmentEffort

Page 10: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

10 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Page 11: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Example

• Test addItemQuantity and removeLineItem methods of Invoice

Customer Addressshipping

invoicesinvoicedCustomer

shipping

11 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

CustomerFirstNameLastNameDiscount

InvoiceaddItemQuantity()

Addressshipping

billing

1

billing

Product

LineItemQuantityUnitPrice

ExtendedPricePercentDiscount

shipping

Page 12: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

A Bunch of Tests / Checks:TestInvoiceLineItems {

testAddItemQuantity_singleQuantity()testAddItemQuantity_severalQuantity{..}testAddItemQuantity_duplicateProduct {..}testAddItemQuantity_differentProduct () {..}testAddItemQuantity_zeroQuantity {..}testAddItemQuantity_severalQuantity_... {..}

12 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

testAddItemQuantity_severalQuantity_... {..}testAddItemQuantity_discountedPrice_... {..}testRemoveItem_noItemsLeft… {..}testRemoveItem_oneItemLeft… {..}testRemoveItem_ severalItemsLeft… {..}

}

Page 13: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Do Your Tests Look Like:public void testAddItemQuantity_severalQuantity () throws Exception {

try {// Setup Fixturefinal int QUANTITY = 5;Address billingAddress = new Address("1222 1st St SW", "Calgary", "Alberta", "T2N 2V2",

"Canada");Address shippingAddress = new Address("1333 1st St SW", "Calgary", "Alberta", "T2N 2V2",

"Canada");Customer customer = new Customer(99, "John", "Doe", new BigDecimal("30"), billingAddress,

shippingAddress);Product product = new Product(88, "SomeWidget", new BigDecimal("19.99"));Invoice invoice = new Invoice(customer);// Exercise SUTinvoice.addItemQuantity(product, QUANTITY);// Verify OutcomeList lineItems = invoice.getLineItems();if (lineItems.size() == 1) {

13 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

if (lineItems.size() == 1) {LineItem actualLineItem = (LineItem)lineItems.get(0);assertEquals(invoice, actualLineItem.getInvoice());assertEquals(product, actualLineItem.getProduct());assertEquals(quantity, actualLineItem.getQuantity()); assertEquals(new BigDecimal("30"), actualLineItem.getPercentDiscount());assertEquals(new BigDecimal("19.99"), actualLineItem.getUnitPrice());assertEquals(new BigDecimal(“69.96"), actualLineItem.getExtendedPrice());

} else {assertTrue(“Invoice should have exactly one line item“, false);

}} finally {

deleteObject(expectedLineItem);deleteObject(invoice);deleteObject(product);deleteObject(customer);deleteObject(billingAddress);deleteObject(shippingAddress); :

} You might be questioning their value!

Page 14: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

How To Get To This?@Testpublic void addItemQuantity_severalQuantity () {

QUANTITY = 5 ;product = givenAnyProduct();invoice = givenAnEmptyInvoice();

invoice.addItemQuantity( product, QUANTITY);

14 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

invoice.addItemQuantity( product, QUANTITY);

assertExactlyOneLineItem(invoice, expectedItem(

invoice, product, QUANTITY, product.getPrice()* QUANTITY) );

}

Page 15: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

The Whole Testpublic void testAddItemQuantity_severalQuantity () throws Exception {

// Setup Fixturefinal int QUANTITY = 5;Address billingAddress = new Address("1222 1st St SW", "Calgary",

"Alberta", "T2N 2V2", "Canada");Address shippingAddress = new Address("1333 1st St SW", "Calgary",

"Alberta", "T2N 2V2", "Canada");Customer customer = new Customer(99, "John", "Doe", new BigDecimal("30"),

billingAddress, shippingAddress);Product product = new Product(88, "SomeWidget", new BigDecimal("19.99"));Invoice invoice = new Invoice(customer);// Exercise SUT

Given: ???

When we call addItemQuantity

15 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

// Exercise SUTinvoice.addItemQuantity(product, QUANTITY);// Verify OutcomeList lineItems = invoice.getLineItems();if (lineItems.size() == 1) {LineItem actualLineItem = (LineItem)lineItems.get(0);assertEquals(invoice, actualLineItem.getInvoice());assertEquals(product, actualLineItem.getProduct());assertEquals(quantity, actualLineItem.getQuantity()); assertEquals(new BigDecimal("30"), actualLineItem.getPercentDiscount());assertEquals(new BigDecimal("19.99"), actualLineItem.getUnitPrice());assertEquals(new BigDecimal(“69.96"),

actualLineItem.getExtendedPrice());} else {assertTrue(“Invoice should have exactly one line item“, false);

} :}

addItemQuantity

Then: ???

Page 16: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Verifying the Outcome

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

assertEquals(invoice, actualLineItem.getInvoice());

assertEquals(product, actualLineItem.getProduct());

assertEquals(quantity, actualLineItem.getQuantity());

assertEquals(new BigDecimal("30"), actualLineItem.getPercentDiscount());

16 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Obtuse Assertion

actualLineItem.getPercentDiscount());

assertEquals(new BigDecimal("19.99"), actualLineItem.getUnitPrice());

assertEquals(new BigDecimal(“69.96"), actualLineItem.getExtendedPrice());

} else {

assertTrue(“Invoice should have exactly one line item“, false);

}

Page 17: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Use Better Assertion

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

assertEquals(invoice, actualLineItem.getInvoice());

assertEquals(product, actualLineItem.getProduct());

assertEquals(quantity, actualLineItem.getQuantity());

assertEquals(new BigDecimal("30"), actualLineItem.getPercentDiscount());

Refactoring

17 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

actualLineItem.getPercentDiscount());

assertEquals(new BigDecimal("19.99"), actualLineItem.getUnitPrice());

assertEquals(new BigDecimal(“69.96"), actualLineItem.getExtendedPrice());

} else {

fail("invoice should have exactly one line item");

}}

Page 18: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Use Better Assertion

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

assertEquals(invoice, actualLineItem.getInvoice());

assertEquals(product, actualLineItem.getProduct());

assertEquals(quantity, actualLineItem.getQuantity());

assertEquals(new BigDecimal("30"), actualLineItem.getPercentDiscount());

Refactoring

18 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Hard-Wired Test Data

actualLineItem.getPercentDiscount());

assertEquals(new BigDecimal("19.99"), actualLineItem.getUnitPrice());

assertEquals(new BigDecimal(“69.96"), actualLineItem.getExtendedPrice());

} else {

fail("invoice should have exactly one line item");

}}

Fragile Tests

Page 19: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Expected Object

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY);

assertEquals(expectedLineItem.getInvoice(), actualLineItem.getInvoice());

assertEquals(expectedLineItem.getProduct(),

Pattern

19 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

assertEquals(expectedLineItem.getProduct(), actualLineItem.getProduct());

assertEquals(expectedLineItem.getQuantity(), actualLineItem.getQuantity());

assertEquals(expectedLineItem.getPercentDiscount(), actualLineItem.getPercentDiscount());

assertEquals(expectedLineItem.getUnitPrice(), actualLineItem.getUnitPrice());

assertEquals(expectedLineItem.getExtendedPrice(), actualLineItem.getExtendedPrice());

} else {

fail("invoice should have exactly one line item");

}}

Page 20: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Verbose Test

Expected Object

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertEquals(expectedLineItem.getInvoice(), actualLineItem.getInvoice());

assertEquals(expectedLineItem.getProduct(),

Pattern

20 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Verbose TestassertEquals(expectedLineItem.getProduct(),

actualLineItem.getProduct());

assertEquals(expectedLineItem.getQuantity(), actualLineItem.getQuantity());

assertEquals(expectedLineItem.getPercentDiscount(), actualLineItem.getPercentDiscount());

assertEquals(expectedLineItem.getUnitPrice(), actualLineItem.getUnitPrice());

assertEquals(expectedLineItem.getExtendedPrice(), actualLineItem.getExtendedPrice());

} else {

fail("invoice should have exactly one line item");

}}

Page 21: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Introduce Custom Assert

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertLineItemsEqual(expectedLineItem, actualLineItem);

Refactoring

21 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

} else {

fail("invoice should have exactly one line item");

}

Custom Assertion

Page 22: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Introduce Custom Assert

List lineItems = invoice.getLineItems();

if (lineItems.size() == 1) {

LineItem actualLineItem = (LineItem)lineItems.get(0);

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertLineItemsEqual(expectedLineItem, actualLineItem);

} else {

fail("invoice should have exactly one line item");

Refactoring

22 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Conditional Test LogicConditional Test Logic

fail("invoice should have exactly one line item");

}

Page 23: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Replace Conditional Logic with Guard Assertion

List lineItems = invoice.getLineItems();assertEquals("number of items",lineItems.size(),1);

LineItem actualLineItem = (LineItem)lineItems.get(0);

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertLineItemsEqual(expectedLineItem, actualLineItem);

Refactoring

23 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Page 24: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

The Whole Testpublic void testAddItemQuantity_severalQuantity () throws Exception {

// Setup Fixturefinal int QUANTITY = 5;Address billingAddress = new Address("1222 1st St SW", "Calgary",

"Alberta", "T2N 2V2", "Canada");Address shippingAddress = new Address("1333 1st St SW",

"Calgary", "Alberta", "T2N 2V2", "Canada");Customer customer = new Customer(99, "John", "Doe", new

BigDecimal("30"), billingAddress, shippingAddress);Product product = new Product(88, "SomeWidget", new

24 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Product product = new Product(88, "SomeWidget", new BigDecimal("19.99"));

Invoice invoice = new Invoice(customer);// Exercise SUTinvoice.addItemQuantity(product, QUANTITY);// Verify Outcome

assertEquals("number of items",lineItems.size(),1);

LineItem actualLineItem = (LineItem)lineItems.get(0);

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY);

assertLineItemsEqual(expectedLineItem, actualLineItem); }

Page 25: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Hard-Coded Test Data

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5;

Address billingAddress = new Address("1222 1st St SW", "Calgary", "Alberta", "T2N 2V2", "Canada");

Address shippingAddress = new Address("1333 1st St SW", "Calgary", "Alberta", "T2N 2V2", "Canada");

Customer customer = new Customer(99, "John", "Doe", new

Code Smell

25 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Customer customer = new Customer(99, "John", "Doe", new BigDecimal("30"), billingAddress, shippingAddress);

Product product = new Product(88, "SomeWidget", new BigDecimal("19.99"));

Invoice invoice = new Invoice(customer);

// Exercise SUT

invoice.addItemQuantity(product, QUANTITY);

Hard-coded Test Data

(Obscure Test)

Unrepeatable Tests

Page 26: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Distinct Generated Values

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;

Address billingAddress = new Address(getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString());

Address shippingAddress = new Address(getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString());

Customer customer = new Customer(

Pattern

26 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Customer customer = new Customer(getUniqueInt(), getUniqueString(), getUniqueString(), getUniqueDiscount(), billingAddress, shippingAddress);

Product product = new Product(getUniqueInt(), getUniqueString(), getUniqueNumber());

Invoice invoice = new Invoice(customer);

Page 27: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Distinct Generated Values

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;

Address billingAddress = new Address(getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString());

Address shippingAddress = new Address(getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString(), getUniqueString());

Customer customer1 = new Customer( Irrelevant

Pattern

27 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Customer customer1 = new Customer(getUniqueInt(), getUniqueString(),getUniqueString(), getUniqueDiscount(), billingAddress, shippingAddress);

Product product = new Product(getUniqueInt(), getUniqueString(), getUniqueNumber());

Invoice invoice = new Invoice(customer);

Irrelevant Information

(Obscure Test)

Page 28: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Creation Method

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5;Address billingAddress = createAnonymousAddress();

Address shippingAddress = createAnonymousAddress();

Customer customer = createCustomer( billingAddress,

Pattern

28 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Customer customer = createCustomer( billingAddress, shippingAddress);

Product product = createAnonymousProduct();

Invoice invoice = new Invoice(customer);

Page 29: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Obscure Test - Irrelevant Informationpublic void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5;Address billingAddress = createAnonymousAddress();Address shippingAddress = createAnonymousAddress(); Customer customer = createCustomer(

billingAddress, shippingAddress);Product product = createAnonymousProduct();Invoice invoice = new Invoice(customer); Irrelevant

Information

Code Smell

29 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

product, QUANTITY, product.getPrice()*QUANTITY );List lineItems = invoice.getLineItems();assertEquals("number of items", lineItems.size(), 1);LineItem actualLineItem = (LineItem)lineItems.get(0);assertLineItemsEqual(expectedLineItem, actualLineItem);

}

Information (Obscure Test)

Page 30: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Remove Irrelevant Information

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;

Customer customer = createAnonymousCustomer();`

Product product = createAnonymousProduct();Invoice invoice = new Invoice(customer); Irrelevant

Refactoring

30 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

product, QUANTITY, product.getPrice()*QUANTITY );List lineItems = invoice.getLineItems();assertEquals("number of items", lineItems.size(), 1);LineItem actualLineItem = (LineItem)lineItems.get(0);assertLineItemsEqual(expectedLineItem, actualLineItem);

}

Irrelevant Information

(Obscure Test)

Page 31: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Remove Irrelevant Information

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;

Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice()

Refactoring

31 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

product, QUANTITY, product.getPrice()*QUANTITY );List lineItems = invoice.getLineItems();assertEquals("number of items", lineItems.size(), 1);LineItem actualLineItem = (LineItem)lineItems.get(0);assertLineItemsEqual(expectedLineItem, actualLineItem);

}

Page 32: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Introduce Custom Assertion

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;

Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice()

Refactoring

32 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Mechanics hides Intent

// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

product, QUANTITY, product.getPrice()*QUANTITY );List lineItems = invoice.getLineItems();assertEquals("number of items", lineItems.size(), 1);LineItem actualLineItem = (LineItem)lineItems.get(0);assertLineItemsEqual(expectedLineItem, actualLineItem);

}

Page 33: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Introduce Custom Assertion

public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;

Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice()

Refactoring

33 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

product, QUANTITY, product.getPrice()*QUANTITY );assertExactlyOneLineItem(invoice, expectedLineItem );

}

Page 34: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

The Whole Test – Done

public void testAddItemQuantity_severalQuantity () {// Setupfinal int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

34 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertExactlyOneLineItem(invoice, expectedLineItem );}

Page 35: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Four-Phase Test

public void testAddItemQuantity_severalQuantity () {// Setupfinal int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

or // Arrange

or // Act

or // Assert

Pattern

35 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertExactlyOneLineItem(invoice, expectedLineItem );}

• Bill Wake http://xp123.com/articles/3a-arrange-act-assert/

This terminology reinforces our focus on mechanics, not intent!

// Teardown// Shouldn’t be needed

Page 36: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Four-Phase Test

public void testAddItemQuantity_severalQuantity () {// Setupfinal int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Exerciseinvoice.addItemQuantity(product, QUANTITY);// VerifyLineItem expectedLineItem = newLineItem(invoice,

Given an empty invoice

when I call addItemQuantity

or // Arrange

or // Act

or // Assert

36 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertExactlyOneLineItem(invoice, expectedLineItem );}

•Use Domain-Specific Language•Say Only What is Relevant

Then the invoice will end up with exactly 1

lineItem on it.

Page 37: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Improving Terminology

public void testAddItemQuantity_severalQuantity () {// Givenfinal int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Wheninvoice.addItemQuantity(product, QUANTITY);// ThenLineItem expectedLineItem = newLineItem(invoice,product,

37 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

LineItem expectedLineItem = newLineItem(invoice,product, QUANTITY, product.getPrice()*QUANTITY );

assertExactlyOneLineItem(invoice, expectedLineItem );}

•Use Domain-Specific Language•Say Only What is Relevant

Page 38: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Improving Terminology@Test public void testAddItemQuantity_severalQuantity () {

final int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Wheninvoice.addItemQuantity(product, QUANTITY);// ThenLineItem expectedLineItem = newLineItem(invoice, product,

Upgrade to Junit 4+

38 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertExactlyOneLineItem(invoice, expectedLineItem );}

•Use Domain-Specific Language•Say Only What is Relevant

Page 39: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Improving Terminology@Test public void addItem_severalQuantity_itemValueIsQuantityTimesProductPrice(){

final int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Wheninvoice.addItemQuantity(product, QUANTITY);// ThenLineItem expectedLineItem = newLineItem(invoice, product,

Rename

39 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

LineItem expectedLineItem = newLineItem(invoice, product, QUANTITY, product.getPrice()*QUANTITY );

assertExactlyOneLineItem(invoice, expectedLineItem );}

•Use Domain-Specific Language•Say Only What is Relevant

Constantly Strive to Improve Readability

Page 40: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Improving Terminology@Test public void addItem_severalQuantity_itemValueIsQuantityTimesProductPrice(){

final int QUANTITY = 5 ;Product product = createAnonymousProduct();Invoice invoice = createAnonymousInvoice();// Wheninvoice.addItemQuantity(product, QUANTITY);// ThenshouldBeExactlyOneLineItemOn(invoice,

Rename, Inline Local, Rename

40 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

shouldBeExactlyOneLineItemOn(invoice, expectedLineItem(invoice, product, QUANTITY,

product.getPrice()*QUANTITY) );}

•Use Domain-Specific Language•Say Only What is Relevant

Constantly Strive to Improve Readability

Page 41: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Improving Terminology@Test public void addItem_severalQuantity_itemValueIsQuantityTimesProductPrice(){

final int QUANTITY = 5 ;Product product = createIrrelevantProduct();Invoice invoice = createIrrelevantInvoice();// Wheninvoice.addItemQuantity(product, QUANTITY);// ThenshouldBeExactlyOneLineItemOn(invoice,

Another Rename

41 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

shouldBeExactlyOneLineItemOn(invoice, expectedLineItem(invoice, product, QUANTITY,

product.getPrice()*QUANTITY) );}

•Use Domain-Specific Language•Say Only What is Relevant

Constantly Strive to Improve Readability

Page 42: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Improving Terminology@Test public void addItem_severalQuantity_itemValueIsQuantityTimesProductPrice(){

final int QUANTITY = 5 ;Product product = givenAnyProduct();Invoice invoice = givenAnEmptyInvoice();// Wheninvoice.addItemQuantity(product, QUANTITY);// ThenshouldBeExactlyOneLineItemOn(invoice,

Yet Another Rename

42 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

shouldBeExactlyOneLineItemOn(invoice, expectedLineItem(invoice, product, QUANTITY,

product.getPrice()*QUANTITY) );}

•Use Domain-Specific Language•Say Only What is Relevant

Naming as a Process – Arlo Belshee

Page 43: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Test CoverageTestInvoiceLineItems {

testAddItemQuantity_singleQuantity()testAddItemQuantity_severalQuantity{..}testAddItemQuantity_duplicateProduct {..}testAddItemQuantity_differentProduct () {..}testAddItemQuantity_zeroQuantity {..}testAddItemQuantity_severalQuantity_... {..}

43 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

testAddItemQuantity_severalQuantity_... {..}testAddItemQuantity_discountedPrice_... {..}testRemoveItem_noItemsLeft… {..}testRemoveItem_oneItemLeft… {..}testRemoveItem_ severalItemsLeft… {..}

}

Page 44: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Test CoverageTestInvoiceLineItems {

addItem_singleQuantity_itemValueIsProductPriceaddItem_severalQuantity_itemValueIsQuantityTimesPr… addItem_duplicateProduct_singleItemHasSumOfQuantityaddItem_differentProduct_oneItemPerProductaddItem_zeroQuantity_noItemAddedaddItem_customerWithDiscount_itemValueIsDiscounted

A whole bunch of Renames

44 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

addItem_customerWithDiscount_itemValueIsDiscountedremoveItem_onlyItem_noItemsLeft…removeItem_severalItems_oneLessItemLeftremoveItem_severalItems_severalItemsLeft

}

Page 45: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Test CoverageTestInvoiceLineItems {

addItem_singleQuantity_itemValueIsProductPriceaddItem_severalQuantity_itemValueIsQuantityTimesPr… addItem_duplicateProduct_singleItemHasSumOfQuantityaddItem_differentProduct_oneItemPerProductaddItem_zeroQuantity_noItemAddedaddItem_customerWithDiscount_itemValueIsDiscounted

45 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

addItem_customerWithDiscount_itemValueIsDiscountedremoveItem_onlyItem_noItemsLeft…removeItem_severalItems_oneLessItemLeftremoveItem_severalItems_severalItemsLeft

}

Page 46: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Rapid Test Writing

final int QUANTITY2 = 2 ;

Given an empty invoice

when I call addItemQuantitytwice with same

product

@Test public void addItem_duplicateProduct_singleItemHasSumOfQuantities () {

final int QUANTITY = 1 ;

Product product = givenAnyProduct();

Invoice invoice = givenAnEmptyInvoice();

// When

invoice.addItemQuantity(product, QUANTITY);

46 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

The invoice will end up with exactly 1 lineItem on it for the sum of the two calls to add..().

invoice.addItemQuantity(product, QUANTITY);

// ThenshouldBeExactlyOneLineItemOn(invoice,

expectedLineItem(invoice, product, QUANTITY, product.getPrice() * QUANTITY) );

}

expectedLineItem(invoice, product, QUANTITY+QUANTITY2, product.getPrice() * (QUANTITY+QUANTITY2) );

invoice.addItemQuantity(product, QUANTITY2);

GGM53

Page 47: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Slide 46

GGM53 Redo using new naming conventionsGerard Meszaros, 12/10/19

Page 48: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Test CoverageTestInvoiceLineItems {

addItem_singleQuantity_itemValueIsProductPrice…{..}addItem_severalQuantity_itemValueIsQuantityTi… {..}addItem_duplicateProduct_singleItemHasSumOfQ...{..}addItem_differentProduct_oneItemPerProduct( ) {..}addItem_zeroQuantity_noItem… {..}addItem_severalQuantity_... {..}

47 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

addItem_severalQuantity_... {..}addItem_discountedPrice_... {..}removeItem_noItemsLeft… {..}removeItem_oneItemLeft… {..}removeItem_ severalItemsLeft… {..}

}

Page 49: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Rapid Test Writing@Test public void

addItem_differentProduct_oneItemPerProduct () {

final int QUANTITY = 1;

Product product1 = givenAnyProduct();

Invoice invoice = givenAnEmptyInvoice();

// When

invoice.addItemQuantity(product1, QUANTITY);

48 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

invoice.addItemQuantity(product1, QUANTITY);

// ThenshouldBeExactlyOneLineItemOn(invoice,

expectedLineItem(invoice, product1, QUANTITY, product1.getPrice() * QUANTITY1)

);}

shouldBeExactlyTwoLineItems(invoice,

expectedLineItem(invoice, product2, QUANTITY2, product2.getPrice() * QUANTITY2 ) );

}

Page 50: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Rapid Test Writing@Test public void

addItem_differentProduct_oneItemPerProduct () {

final int QUANTITY = 1;

Product product1 = givenAnyProduct();

Invoice invoice = givenAnEmptyInvoice();

// When

invoice.addItemQuantity(product1, QUANTITY);

Product product2 = givenAnyProduct();when I call

addItemQuantitytwice with

different products

final int QUANTITY2 = 2;

Given an empty invoice

49 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

invoice.addItemQuantity(product1, QUANTITY);

// ThenshouldBeExactlyOneLineItemOn(invoice,

expectedLineItem(invoice, product1, QUANTITY, product1.getPrice() * QUANTITY1)

);}

shouldBeExactlyTwoLineItems(invoice,

invoice.addItemQuantity(product2, QUANTITY2);

expectedLineItem(invoice, product2, QUANTITY2, product2.getPrice() * QUANTITY2 ) );

}

The invoice will end up with 2 lineItems on it, one for each of the two calls to add..().

Page 51: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Removing Deodorant@Test public void

addItem_differentProduct_oneItemPerProduct () {

final int QUANTITY = 1;

Product product1 = givenAnyProduct();

Invoice invoice = givenAnEmptyInvoice();

invoice.addItemQuantity(product1, QUANTITY);

Product product2 = givenAnyProduct();when I call

addItemQuantitytwice with

different products

final int QUANTITY2 = 2;

Given an empty invoice

50 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

invoice.addItemQuantity(product1, QUANTITY);

shouldBeExactlyOneLineItemOn(invoice,expectedLineItem(invoice, product1, QUANTITY,

product1.getPrice() * QUANTITY) );

}

shouldBeExactlyTwoLineItems(invoice,

invoice.addItemQuantity(product2, QUANTITY2);

expectedLineItem(invoice, product2, QUANTITY2, product2.getPrice() * QUANTITY2 ) );

}

The invoice will end up with 2 lineItems on it, one for each of the two calls to add..().

Page 52: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Benefits• Writing tests is faster

– Less code to write

• Reading tests is faster, too.– Less code to read

• Much easier to see what’s different from one test to another.

51 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

test to another.– Differences are fairly obvious

• Tests are much less fragile– Most code breakages are in test utility methods, not the

tests themselves.

Nice, But Couldn’t We Avoid the Refactoring?

Page 53: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

@Testpublic void generateInvoice_should…() throws Ex… {

// setup and exercise omitted

Reducing the Need to Refactor Tests

// verify the actual invoice header matches the expected headerassertNotNull(“Number”, newInvoice.getNumber());assertEquals(“Name”, account. getName(), newInvoice.getName());assertEquals(“Address”, account. getAddr(), newInvoice.getAddr());

assertEquals(“City”, account. getC

52 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Hmmm, this is getting ugly!Let’s try another way ….

assertEquals(“City”, account. getC

Page 54: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

@Testpublic void generateInvoice_should…() throws Ex… {

// setup and exercise omittedassertInvoiceHeaderIs( newInvoice , expectedHeader(account) );

Reducing the Need to Refactor Tests

shouldBeExactlyTwoLineItemsOn(invoice,expectedLineItem( invoice, product1, QUANTITY,

product1.getPrice() * QUANTITY1)

53 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

} That’s Better!

product1.getPrice() * QUANTITY1)expectedLineItem( invoice, product2, QUANTITY2,

product2.getPrice() * QUANTITY2) );

Now, All I have to do is implement these test utility methods (test-driven, of course!)

Page 55: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

What Does it Take To be Successful?Programming Experience

+ XUnit Experience

+ Testing experience

+ Good naming

+Regular refactoring

54 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

+Regular refactoring

+ a bunch of other things …

+ Fanatical Attention to Test Maintainability

-------------------------------

Robust Automated Tests ->

Page 56: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Now, you may be thinking,

“This is fine for unit tests,

55 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

“This is fine for unit tests, but how does it help us with

system tests?”

Page 57: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Example: Notifying of Bank Transactions

Configure Notification

Rules

Suspend Notification

Not

ify

base

d on

cha

rge

Type

Not

ify

base

d on

cha

rge

Cont

inen

tN

otif

y ba

sed

on c

harg

e Co

untr

yN

otif

y ba

sed

on c

harg

e Ci

ty &

Sta

te

Not

ific

atio

n

56 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

AccountHolder

Notification

Resume Notification

Process Transaction

TransactionSettlement

Not

ify

base

d on

cha

rge

Type

Not

ify

base

d on

cha

rge

Cont

inen

tN

otif

y ba

sed

on c

harg

e Co

untr

yN

otif

y ba

sed

on c

harg

e Ci

ty &

Sta

te

Susp

end

Not

ific

atio

n

Page 58: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Checking Notifications• Open MegaBank app

• Log in as “BobMa” with password ******

• Click on “Manage Notifications” tab

• Click on “Add New Rule” button

• Select account “10035692877”

• Type “Default Rule” into field “rule name”

57 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

• Type “Default Rule” into field “rule name”

• Type “1000” into field “threshold amount”

• Click on “all transaction types” radio button

• Click on “all locations” radio button

• Click on “save changes” button

• …

• …

Page 59: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Checking Notifications – 1/2Given:

User and Accounts

Another attempt:

When: Notification

Rule is Configured

58 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Then:Notification Rule Configured and

Active

Then:Notification Rule

is Active

Page 60: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Checking Notifications – 2/2

Then: Expected

When: The Transactions to be processed

Another attempt:

59 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Then: ExpectedNotifications

Page 61: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Test Automation Pyramid

Exploratory Tests

Component

SystemTests (GUI)

60 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Pyramid originally proposed by Mike Cohn

Unit Tests

ComponentTests (API)

Page 62: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Behavior Specification at Right Level

Component

SystemTests (GUI)

Det

ail

High

Low

61 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Unit Tests

ComponentTests (API)

Broad Narrow

Det

ail

High

Low

Scope

Page 63: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Behavior Specification at Right LevelDet

ail

High

Low

System

62 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Broad Narrow

Det

ail

High

Low

Scope

ComponentTests (API)

SystemTests (GUI)

Page 64: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Behavior Specification at Right Level

System

Multi-Use Case

Workflows

IncompleteSpec

Det

ail

High

Low

Transactions

63 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

ComponentTests (API)

SystemTests (GUI)

Rules &Algorithms

Toomuch detailUnmaintainable

Det

ail

High

Low

Broad NarrowScope

Transactions(Use Cases)

Page 65: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Changing Level of Abstraction/Detail

Multi-Use Case

WorkflowsTransactions

IncompleteSpec

Det

ail

High

L

ow

64 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Toomuch detailUnmaintainable

Rules &Algorithms

Transactions(Use Cases)

Broad Narrow

Det

ail

High

L

ow

Scope

Page 66: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Refactoring Workflow ExampleGiven:

User and Accounts

When: Notification

Rule is Configured

65 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Then:Notification Rule Configured and

Active

Then:Notification Rule

is Active

Page 67: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Refactoring Workflow Example

66 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

“If it isn’t essential to conveying the essence of the behavior, it is essential to

not include it.”

Page 68: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Then: Expected

When: The Transactions to be processed

Refactoring Workflow Example

67 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Then: ExpectedNotifications

Page 69: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Refactoring Workflow Example

68 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Page 70: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Refactoring Workflow Example

69 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Page 71: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Refactoring Workflow ExampleGiven: User &

Thresholds

When: Transactions

Are Processed

70 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Then: We Expect

Notifications

Page 72: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Given: User &

Thresholds

When: Transactions

Are Processed

Refactoring Workflow Example

71 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

9991113333

Then: We Expect

Notifications

credit

credit

Page 73: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Refactoring Workflow ExampleGiven: User &

Thresholds

When: Transactions

Are Processed

72 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

9991113333

Broad Scope (Multi-Actor);

Minimum Detail (per Actor/Transaction);

Then: We Expect

Notifications

credit

credit

Page 74: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Filling in the Details

Multi-Use Case

WorkflowsTransactions

IncompleteSpec

Det

ail

High

Low

73 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Rules &Algorithms

Transactions(Use Cases)

Too much detailUnmaintainableBroad Narrow

Det

ail

High

Low

Scope

Page 75: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Business Rule Example

Process Transaction

Threshold per Charge Type

Configuration

74 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Page 76: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Process Transaction

Threshold per Charge Type

Example:

Configuration Given these rules

Business Rule ExampleWhen we ask

NotificationRequired? with this transaction:

75 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Narrow Scope (Single Rule)

High Detail (Everything that matters)Then: The

answer should be

Page 77: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Closing Thoughts• Are your automated checks helping you

deliver value continuously?– Do they help you understand what you need to deliver?

• Are your checks helping Make Safety a Prerequiste?– Are they making it safer to change your code?

76 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

– Are they making it safer to change your code?

• Are they helping you Experiment and Learn Continuously?– Fast feedback on impacts of code changes?

• Are you Making People Awesome by

automating the checks?– Happy developers and users?

Page 78: Unit Test Craftsmanship-Finalxunitpatterns.com/~gerard/AgileTourVietnam2016.pdf · (PEHGGHG 7HOHFRP 0\ %DFNJURXQG 6RIWZDUH GHYHORSHU 'HYHORSPHQW PDQDJHU 3URMHFW 0DQDJHU 6RIWZDUH DUFKLWHFW

Thank You!Gerard Meszaros

[email protected] http://www.xunitpatterns.com

Slides: http://vietnam2016.xunitpatterns.com

Jolt Productivity Award winner - Technical Books

77 Copyright 2016 Gerard Meszaroshttp://vietnam2016.xunitpatterns.com

Call me when you:• Want to transition to Agile or Lean• Want to do Agile or Lean better• Want to teach developers how to test• Need help with test automation strategy• Want to improve your test automation

winner - Technical Books

Available on MSDN: