test driven lasse koskela chapter 4: concepts and patterns for tdd

27
Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD Paul Ammann http://cs.gmu.edu/~pammann/

Upload: hisoki

Post on 24-Feb-2016

38 views

Category:

Documents


0 download

DESCRIPTION

Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD. Paul Ammann http://cs.gmu.edu/~pammann/. Overview. How to Write Tests and Make Them Pass Essential Testing Concepts Closer Look into Test Doubles Guidelines for Testable Designs Unit-Testing Patterns - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

Test DrivenLasse Koskela

Chapter 4: Concepts and Patterns for TDD

Paul Ammann

http://cs.gmu.edu/~pammann/

Page 2: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 2

Overview• How to Write Tests and Make Them Pass• Essential Testing Concepts• Closer Look into Test Doubles• Guidelines for Testable Designs• Unit-Testing Patterns• Working With Legacy Code

Lots of Stuff in This Chapter

Page 3: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 3

How to Write Tests And Make Them Pass• Test Selection Strategies

– Details-First vs. Big-Picture• Details-First Offers Concrete Progress, but• Big-Picture Fleshes Out Overall Design• Judgment Call In Any Given Situation

– Uncertain vs. Familiar• Attacking Uncertainty Can Reduce Risk• Familiar Code May Have Large Payoff

– High Value vs. Low Hanging Fruit• Usually, High Value is Better

– Happy Path vs. Error Situations• Happy Path First – Mostly for Value• Sometimes Error Situations Help Define Remainder as Happy Path

Keep Your Options in Mind; Avoid a Rut

Page 4: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 4

How to Write Tests And Make Them Pass(2)

• Implementation Strategies– Faking It

• Useful Strategy For Handling Big Steps• Incurs Later Obligations To Squeeze Out Fakes

– Triangulation• Strategy For Evolving Towards More General Implementation• Use To Drive Out Hard Coded Solutions

– Obvious Implementation• Sometimes The Correct Solution Really Is Simple• Try It And See

– If Tests Fail, Back It Out

Goal Is To Get To Green Fast

Page 5: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 5

How to Write Tests And Make Them Pass(3)• Prime Guidelines for Test Driving

– Do. Not. Skip. Refactoring.• The Design Cycle for TDD is Crucial• Skipping This Step Leads to Code/Test Bloat• Result Is Unmaintainable Software

– Get To Green Fast• Write Code That Passes The Tests First• Then Worry About Refactoring

– Slow Down After a Mistake• Mistakes Indicate That Your Reach Exceeds Your Grasp• Back Off and Try Smaller Steps• The Tests Are The Oracle That Keeps You On Track

The First Rule Is The Most Important

Page 6: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 6

Essential Testing Concepts• Fixtures are the Context for Tests

– Holistic View of State– Fixtures Remove Duplication

• Bloated Tests Are Hard to Read and Hard to Maintain– Fixtures Allow For Focused Tests

• Test Doubles Stand In for Dependencies– Example: java.sql.ResultSet– Details Depend on Database Used– More on Doubles in Later Slides

Real-World JUnit Tests Can’t Avoid Some Complexities

Page 7: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 7

Essential Testing Concepts(2)• State and Interaction-Based Testing

– State-Based Testing• Idea Is To Look At Variable State To Verifying Result@Testpublic void notEmpty() { Collection<String> c = new ArrayList<String>(); assertTrue (c.isEmpty()); c.add(“Bob”); assertFalse(c.isEmpty());}

– Testing For Interactions• Goal: Did The Expected Methods Calls Happen In The Right Order?• Usually Requires Some Sort of Double

We lean on interaction-based testing to verify how an object talks to its collaborators; we lean on state-based testing to verify how well the object listens.

Page 8: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 8

Closer Look Into Test Doubles

• Replace Object with Double If Real Object is– Too Slow– It’s Not Available– It Depends on Something That’s Not Available– Its Too Difficult To Instantiate Or Configure For a Test

• Examples – How to test exceptions, such as

» “Dead”code?» An unplugged network cable?

– How to interact with something that’s nondeterministic?

Doubles Are Key To Unit Testing

Page 9: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 9

Closer Look Into Test Doubles(2)

• Example of a Test Double– Next Slide

• Stubs, Fakes, and Mocks– Stub: Simplest Possible Implementation– Fake: Still Hand Coded, But A Degree More Sophisticated– Mock: Usually Generated By Tools

• Mock Objects in Action– Next slide

Checking Correct Behavior is Complicated!

Page 10: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 10

Test Double Example: State Testing

Page 11: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 11

Test Double Example: Interaction Testing

Page 12: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 12

Guidelines for Testable Designs

• Choose Composition Over Inheritance– General Advice For Normal Coding And Testing– More Verbose, But Worth It

• Avoid Static and Singleton– These Are Hard To Double

• Static class names are hardcoded. How to replace at test time?– This Can Conflict With Normal Coding Advice

• Consider Factories!

Test Requirements Impact Code!

Page 13: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 13

Dependency Example: Static Methodpublic class OrderProcessor { public void process(Order order) { PricingService service = PricingService.getInstance(); // use the PricingService object for processing the order } }

4.4. Code smell: methods obtaining dependencies through static method calls

Page 14: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 14

Exploiting a Seam

Page 15: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 15

Injecting Dependencies: Easy Test!

public class OrderProcessorTest { @Test public void testOrderProcessorWithDependencyInjection() throws Exception

{ OrderProcessor p = new OrderProcessor(); p.setPricingService(new FakePricingService()); ... }}

Page 16: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 16

Unit Testing Patterns

• Assertion Patterns– Resulting State Assertion

• Most Common Usage– Guard Assertion

• Test Both Before and After The Action (precondition testing)– Delta Assertion

• Verify Part of the State – Eg, List is One Bigger Than Before– Custom Assertion

• Encodes Complex Verification Rules– Interaction Assertion

• Verification For Interaction Tests

Choose and Use Standard Patterns

Page 17: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 17

Example Interaction Assertion

Page 18: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 18

Fowler: Conventional JUnit Example

public class OrderStateTester extends TestCase { private static String TALISKER = "Talisker"; private static String HIGHLAND_PARK = "Highland Park"; private Warehouse warehouse = new WarehouseImpl(); protected void setUp() throws Exception { warehouse.add(TALISKER, 50); warehouse.add(HIGHLAND_PARK, 25); } public void testOrderIsFilledIfEnoughInWarehouse() { Order order = new Order(TALISKER, 50); order.fill(warehouse); assertTrue(order.isFilled()); assertEquals(0, warehouse.getInventory(TALISKER)); } public void testOrderDoesNotRemoveIfNotEnough() { Order order = new Order(TALISKER, 51); order.fill(warehouse); assertFalse(order.isFilled()); assertEquals(50, warehouse.getInventory(TALISKER)); }}

4.4. Code smell: methods obtaining dependencies through static method calls

Page 19: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 19

Fowler: jMock Versionpublic class OrderInteractionTester extends MockObjectTestCase { private static String TALISKER = “Talisker”; public void testFillingRemovesInventoryIfInStock() { //setup - data Order order = new Order(TALISKER, 50); Mock warehouseMock = new Mock(Warehouse.class); // constructor //setup - expectations warehouseMock.expects(once()).method("hasInventory") .with(eq(TALISKER),eq(50)) .will(returnValue(true)); warehouseMock.expects(once()).method("remove") .with(eq(TALISKER), eq(50)) .after("hasInventory"); //exercise order.fill((Warehouse) warehouseMock.proxy()); //verify warehouseMock.verify(); assertTrue(order.isFilled()); } public void testFillingDoesNotRemoveIfNotEnoughInStock() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); // vs. mock call warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); assertFalse(order.isFilled()); }}

4.4. Code smell: methods obtaining dependencies through static method calls

Page 20: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 20

Fowler: EasyMock Examplepublic class OrderEasyTester extends TestCase { private static String TALISKER = "Talisker"; private MockControl warehouseControl; private Warehouse warehouseMock; public void setUp() { warehouseControl = MockControl.createControl(Warehouse.class); warehouseMock = (Warehouse) warehouseControl.getMock(); } public void testFillingRemovesInventoryIfInStock() { //setup - data Order order = new Order(TALISKER, 50); //setup - expectations warehouseMock.hasInventory(TALISKER, 50); warehouseControl.setReturnValue(true); warehouseMock.remove(TALISKER, 50); warehouseControl.replay(); //exercise order.fill(warehouseMock); //verify warehouseControl.verify(); assertTrue(order.isFilled()); }

4.4. Code smell: methods obtaining dependencies through static method calls

Page 21: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 21

Fowler/Meszaros “Double” Definitions

• A Test Double is anything that stands in for a real object– Dummy

• Used to fill parameter lists– Fake

• Actual working implementations, but take shortcuts• Example: In Memory Database

– Stub• Canned answers to calls made during tests, but useless elsewhere

– Mock• Objects preprogrammed with expectations that form a specification of

the calls they expect to receive

Various Levels of Complexity

Page 22: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 22

Fowler: Stub Examplepublic interface MailService { public void send (Message msg); } public class MailServiceStub implements MailService { private List<Message> messages = new ArrayList<Message>(); public void send (Message msg) { messages.add(msg); } public int numberSent() { return messages.size(); } }

// Usageclass OrderStateTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); }

4.4. Code smell: methods obtaining dependencies through static method calls

Page 23: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 23

Fowler: Mock Versionclass OrderInteractionTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); Mock mailer = mock(MailService.class); order.setMailer((MailService) mailer.proxy()); mailer.expects(once()).method("send");

warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false));

order.fill((Warehouse) warehouse.proxy()); } }

4.4. Code smell: methods obtaining dependencies through static method calls

Page 24: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 24

Unit Testing Patterns (2)

• Fixture Patterns– Parameterized Creation Method

• Populating Complex Set Of Objects– Object Mother

• Aggregate of Creation Methods– Automated TearDown

• More Important For Integration Testing Than Unit Testing

Fixtures Need Attention Too

Page 25: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 25

Unit Testing Patterns (3)• Test Patterns

– Parameterized Test (see next slide)– Self Shunt

• The Double Is The Test Class– Intimate Inner Class

• Sharing Between Test Class and Test Double Classes– Privileged Access

• Reflection-Based Injection Approaches For Legacy Code (Careful)– Extra Constructor

• Compensates For Classes Not Designed For Testing

Tests Need To Accommodate A Variety of Real Code

Page 26: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 26

Parameterized JUnit Example

Page 27: Test Driven Lasse Koskela Chapter 4: Concepts and Patterns for TDD

04/22/2023 27

Working With Legacy Code

• Test-Driven Legacy Development• Analyzing the Change

– Change Points and Inflection Points• Change Code at Change Points• See Effects At Inflection Points• Hopefully, The Points Are Close Together…

• Preparing for the Change– Install Tests To Capture Current Behavior of Inflection Point

• Test-Driving the Change– Add Tests To Capture New Behavior, Also At Inflection Point

Turning Legacy Code Into TDD Code