13
Test Driven Development www.renaissancesoftware.net [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training attendees. Presented at Agile China October 14, 2010 Test Driven Development for Embedded C Presented by James Grenning at Agile China 2010 twitter: jwgrenning 1 http://pragprog.com/titles/jgade/ 1 Monday, October 18, 2010 www.renaissancesoftware.net [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. The Physics of Debug Later Programming (DLP) As Td increases, Tfind increases dramatically •Tfix is usually short, but can increase with Td 2 Bug discovery Mistake made (bug injection) Bug found Bug fixed T d T find T fix Time 2 Monday, October 18, 2010 www.renaissancesoftware.net [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. The Physics of Test Driven Development When Td approaches zero, Tfind approaches zero In many cases, bugs are not around long enough to be considered bugs. • See: http://www.renaissancesoftware.net/blog/archives/16 3 Mistake discovery Mistake made Root cause found Mistake fixed T d T find T fix Time 3 Monday, October 18, 2010 Test Driven Development www.renaissancesoftware.net [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training attendees. Presented at Agile China October 14, 2010 TDD Micro Cycle Write a test Watch it fail Make it pass Refactor (clean up any mess) Repeat until done 4 4 Monday, October 18, 2010 Test Driven Development www.renaissancesoftware.net [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training attendees. Presented at Agile China October 14, 2010 TDD State Machine in C/C++ 5 Write the test Make the test link Make the test compile Compilation error Link error New test fails Make the test pass Refactor (Make it right) All tests pass All tests pass No more tests Choose a test Start Compilation error Link error DONE! Programming error Compiles Clean 5 Monday, October 18, 2010 Test Driven Development www.renaissancesoftware.net [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training attendees. Presented at Agile China October 14, 2010 Test Driving a Circular Buffer (FIFO) 6 23 7 66 12 99 16 90 Out-Index In-Index 99 6 Monday, October 18, 2010

Test Driven Development for Embedded C€¦ · Test Driven Development [email protected] Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

  • Upload
    others

  • View
    9

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Test Driven Development for Embedded C

Presented by James Grenning

at Agile China 2010

twitter: jwgrenning

1

http://pragprog.com/titles/jgade/

1Monday, October 18, 2010

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved.

The Physics of Debug Later Programming (DLP)

• As Td increases, Tfind increases dramatically• Tfix is usually short, but can increase with Td

2

Bug discoveryMistake made(bug injection)

Bug found Bug fixed

Td Tfind T fix

Time

2Monday, October 18, 2010

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved.

The Physics of Test Driven Development

• When Td approaches zero, Tfind approaches zero• In many cases, bugs are not around long enough to be considered

bugs.• See: http://www.renaissancesoftware.net/blog/archives/16

3

Mistake discovery

Mistake made

Root cause found

Mistake fixed

T d Tfind T fix

Time

3Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

TDD Micro Cycle

• Write a test• Watch it fail• Make it pass• Refactor (clean up any mess)• Repeat until done

4

4Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

TDD State Machine in C/C++

5

Write the test

Make the test link

Make the test compile

Compilation error

Link error

New test failsMake the test pass

Refactor(Make it right)

All tests pass

All tests pass No more tests

Choose a test

Start

Compilation error

Link error

DONE!

Programming error

Compiles Clean

5Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Test Driving a Circular Buffer (FIFO)

6

23 7 66 12 99 16 90

Out-Index In-Index

99

6Monday, October 18, 2010

Page 2: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Create a Test-List(avoid analysis paralysis)

7

Circular Buffer Tests

Initially Empty Transition to empty Transition to Full Transition from Full Put-Get FIFO Put to full Get from empty Filled but not wrapped around Wrap around

7Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Set up a Test Fixture

8

TEST_GROUP(CircularBuffer){ CircularBuffer* buffer;

void setup() { buffer = CircularBuffer_Create(); }

void teardown() { CircularBuffer_Destroy(buffer); }};

TEST(CircularBuffer, TestName){}

8Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make a bare Skeleton of the Production Code

9

#ifndef D_CircularBuffer_H#define D_CircularBuffer_H

typedef struct CircularBuffer CircularBuffer;

CircularBuffer * CircularBuffer_Create(int capacity);void CircularBuffer_Destroy(CircularBuffer *);

#endif // D_CircularBuffer_H

9Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make a bare Skeleton of the Production Code

10

#include "CircularBuffer.h"#include <stdlib.h>

struct CircularBuffer{ int dummy;} ;

CircularBuffer* CircularBuffer_Create(){ CircularBuffer* self = calloc(capacity, sizeof(CircularBuffer)); return self;}

void CircularBuffer_Destroy(CircularBuffer* self){ free(self);}

10Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Choose a Test

11

Write the test

Make the test link

Make the test compile

Compilation error

Link error

New test failsMake the test pass

Refactor(Make it right)

All tests pass

All tests pass No more tests

Choose a test

Start

Compilation error

Link error

DONE!

Programming error

Compiles Clean

11Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Which Test?

12

Out-Index In-Index

41 59 14 33 7 31

In-Index Out-Index

12Monday, October 18, 2010

Page 3: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Production Code is Grown One Test at a Time

13

Code without

new feature

Code withnew tested

feature

13Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Write a Test

14

Write the test

Make the test link

Make the test compile

Compilation error

Link error

New test failsMake the test pass

Refactor(Make it right)

All tests pass

All tests pass No more tests

Choose a test

Start

Compilation error

Link error

DONE!

Programming error

Compiles Clean

14Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Start with a Test That is Easy to Get to Pass -- Designing the Interface

15

TEST_GROUP(CircularBuffer){ CircularBuffer* buffer;

void setup() { buffer = CircularBuffer_Create(); }

void teardown() { CircularBuffer_Destroy(buffer); }};

TEST(CircularBuffer, ShouldBeEmptyAfterCreate){ CHECK(CircularBuffer_IsEmpty(buffer));}

15Monday, October 18, 2010

Write the test

Make the test link

Make the test compile

Compilation error

Link error

New test failsMake the test pass

Refactor(Make it right)

All tests pass

All tests pass No more tests

Choose a test

Start

Compilation error

Link error

DONE!

Programming error

Compiles Clean

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make the Test Compile

16

16Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make the Test Compile

17

#ifndef D_CircularBuffer_H#define D_CircularBuffer_H

typedef struct CircularBuffer CircularBuffer;

CircularBuffer * CircularBuffer_Create(int capacity);void CircularBuffer_Destroy(CircularBuffer *);int CircularBuffer_IsEmpty(CircularBuffer *);

#endif // D_CircularBuffer_H

17Monday, October 18, 2010

Write the test

Make the test link

Make the test compile

Compilation error

Link error

New test failsMake the test pass

Refactor(Make it right)

All tests pass

All tests pass No more tests

Choose a test

Start

Compilation error

Link error

DONE!

Programming error

Compiles Clean

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make the Test Link

18

18Monday, October 18, 2010

Page 4: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make the Test Linkand Intentionally Fail

19

#include "CircularBuffer.h"#include <stdlib.h>

struct CircularBuffer{ int dummy;} ;

... not all code shown ...

int CircularBuffer_IsEmpty(CircularBuffer* self){ return 0;}

19Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Watch it Fail

20

CircularBufferTest.cpp:48: error: Failure in TEST(CircularBuffer, EmptyAfterCreation)

CHECK(CircularBuffer_IsEmpty(buffer)) failed

20Monday, October 18, 2010

Write the test

Make the test link

Make the test compile

Compilation error

Link error

New test failsMake the test pass

Refactor(Make it right)

All tests pass

All tests pass No more tests

Choose a test

Start

Compilation error

Link error

DONE!

Programming error

Compiles Clean

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make the Test Pass

21

21Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Write no More Code than is Necessary to Pass the Current Test

• Hard code behavior or partial implementations are OK and often preferred.

22

22Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Make the Test Pass

23

#include "CircularBuffer.h"#include <stdlib.h>

struct CircularBuffer{ int dummy;} ;

... not all code shown ...

int CircularBuffer_IsEmpty(CircularBuffer* self){ return 1;}

23Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Repeat until You Run Out of Tests

• The first tests drive the interface definition• Later tests complete the behavior• You will think of more tests as you go

– Write the new test– or Add it to the test list

24

24Monday, October 18, 2010

Page 5: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Tests are FIRST

Fast

Independent

Repeatable

Self-validating

Timely

25

Thank you: Tim Ottinger

25Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Adaptation for Embedded Software

What is scarce during embedded development?

26

26Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Hardware is Scarce!

• It does not exist.• It is being used by

someone else.•

27

27Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Hardware is Scarce!

• It does not exist.• It is being used by

someone else.•

28

28Monday, October 18, 2010

bool BlindsScheduler::DoesLightRespondToday(TimeService::Day reactionDay){ int today = timeService->GetDay();

if (reactionDay == TimeService::EVERYDAY) return true; if (reactionDay == today) return true; if (reactionDay == TimeService::WEEKEND && (TimeService::SATURDAY == today || TimeService::SUNDAY == today)) return true; if (reactionDay == TimeService::WEEKDAY && today >= TimeService::MONDAY && today <= TimeService::FRIDAY) return true; return false;}

void BlindsScheduler::CheckEvent(ScheduledBlindsEvent* blindsEvent){ if (blindsEvent->extraRandomEventPreventer > 0) { blindsEvent->extraRandomEventPreventer--; return; }

if (!DoesLightRespondToday(blindsEvent->day)) return; if (timeService->GetMinute() != blindsEvent->minute + blindsEvent->randomMinutes) return;

switch (blindsEvent->blindsType) { HorizontalBlinds* theBlinds; case Horizontal: switch (blindsEvent->operation) { case Raise:

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Hardware Has Bugs of its Own

29

29Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Tests Must be Fastand We Can Run Them NOW

30

30Monday, October 18, 2010

Page 6: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Use Your Development Systemfor a Test Bed

• Multi-targeted code.• Must beware of hardware and OS dependencies.• Object Oriented approach to Dependency

Management.• Avoid inefficiencies

– Waiting for hardware.– Waiting for restart.– Waiting for downloads.– Waiting for long compiles.– Debugging on the target.

31

31Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

But There are Risks with Development System Tests

• Architecture differences– Word size– Big-endian Little-endian– Alignment

• Compiler differences• Library differences• Execution differences

32

32Monday, October 18, 2010

Write a TestMake it Pass

Refactor

Stage 1

Compile for TargetProcessor

Stage 2

Run Tests in the Eval Hardware

Stage 3

Run Testsin Target Hardware

Stage 4

AcceptanceTests

Stage 5

More Frequent Less Frequent

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

TDD Adaptation for Embedded Development

33

33Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

TDD Adaptation for Embedded Development

34

See : http://renaissancesoftware.net/files/articles/ProgressBeforeHardware.pdf

Write a TestMake it Pass

Refactor

Stage 1

Compile for TargetProcessor

Stage 2

Run Tests in the Eval Hardware

or Simulator

Stage 3

Run Testsin Target Hardware

Stage 4

Run ManualTests in Target

Stage 5

More Frequent Less Frequent

34Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Automate Stages 2-5

35

DeveloperWorkstation

Source CodeRepository

Continuous IntegrationServer

Target System

Eval/ReferenceSystem

Simulator

Test ManagementSystem

35Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

TDD and the Code in the Middle

(most of the code!)

36

36Monday, October 18, 2010

Page 7: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

What about Code in the Middle

The Module under testis tested independent of other modules

37

37Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Testing a Module in the Middle

The test case takes the role of the client

The servers might need to be stubbed out

38

38Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

A Module with Collaborators

• Every minute, the RTOS wakes up the Light Scheduler.

• If it is time for one of the lights to be controlled, the LightController is told to turn on/off the light.

39

Time Service

+ getDay()+ getTimeOfDay()+ setPeriodicAlarm()

LightScheduler

+addSchedule()+removeSchedule()+wakeUp()

Light Controller

+ on(id)+ off(id)

Hardware RTOS

<<anonymous callback>>

Admin Console

39Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Program to Interfaces

• Separate interface and implementation as separate entities.

• This design has good separation of responsibilities

40

<<interface>>

Time Service

+ getDay()+ getTimeOfDay()+ setPeriodicAlarm()

LightScheduler

+addSchedule()+removeSchedule()+wakeUp()

<<interface>>

Light Controller

+ on(id)+ off(id)

Hardware RTOS

<<anonymous callback>>

Real Light Controller

Real Time Service

<<implements>> <<implements>>

Admin Console

40Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Testing a Module with Collaborators

• Use the real collaborators if you can.

• Use fakes when you must.

41

<<interface>>

Time Service

+ getDay()+ getTimeOfDay()+ setPeriodicAlarm()

LightScheduler

Test

LightScheduler

+addSchedule()+removeSchedule()+wakeUp()

<<interface>>

Light Controller

+ on(id)+ off(id)

Light Controller Spy

FakeTime Service

<<implements>> <<implements>>

41Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Testing the Scheduler

42

TEST(LightScheduler, ScheduleOnTodayNotTimeYet){ LightScheduler_ScheduleTurnOn(3, EVERYDAY, 1000); FakeTimeSource_SetMinute(999);

LightScheduler_Wakeup();

LONGS_EQUAL(LIGHT_NA, FakeLightController_getState(3));}

TEST(LightScheduler, ScheduleOnTodayItsTime){ LightScheduler_ScheduleTurnOn(3, EVERYDAY, 1000); FakeTimeSource_SetMinute(1000);

LightScheduler_Wakeup();

LONGS_EQUAL(LIGHT_ON, FakeLightController_getState(3));}

42Monday, October 18, 2010

Page 8: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Light Controller Interface

43

#ifndef D_LightController_H#define D_LightController_H

enum { MAX_LIGHTS = 32 };

void LightController_Create(void);void LightController_Destroy();void LightController_On(int id);void LightController_Off(int id);

#endif // D_LightController_H

43Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Fake Light Controller Interface

44

#ifndef D_FakeLightController_H#define D_FakeLightController_H

#include "LightController.h"

typedef enum { NO_ID = -1, UNKNOWN_STATE = -1, LIGHT_OFF = 0, LIGHT_ON = 1} LightState;

int FakeLightController_getLastId(void);int FakeLightController_getLastState(void);int FakeLightController_getState(int id);

#endif // D_FakeLightController_H

44Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Fake Light Controller Implementation

45

#include "FakeLightController.h"#include "memory.h"

static int lastId;static int lastLevel;

void LightController_Create(void){ lastId = NO_ID; lastLevel = UNKNOWN_STATE;}

void LightController_Destroy(){}

45Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Fake Light Controller Implementation

46

int FakeLightController_getLastId(void) { return lastId;}

int FakeLightController_getLastState(void) { return lastLevel;}

void LightController_On(int id) { lastId = id; lastLevel = LIGHT_ON;}

void LightController_Off(int id) { lastId = id; lastLevel = LIGHT_OFF;}

46Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Testing the Scheduler

47

TEST(LightScheduler, NoScheduleNothingHappens){ LightScheduler_Wakeup(); LONGS_EQUAL(NO_ID, FakeLightController_getLastId()); LONGS_EQUAL(UNKNOWN_STATE, FakeLightController_getLastState());}

TEST(LightScheduler, ScheduleOnTodayNotTimeYet){ LightScheduler_ScheduleTurnOn(3, EVERYDAY, 1200); FakeTimeService_SetMinute(1199); LightScheduler_Wakeup(); LONGS_EQUAL(NO_ID, FakeLightController_getLastId()); LONGS_EQUAL(UNKNOWN_STATE, FakeLightController_getLastState());}

47Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Testing the Scheduler

48

TEST(LightScheduler, ScheduleWeekdayItsSunday){ LightScheduler_ScheduleTurnOn(3, EVERYDAY, 1200); FakeTimeService_SetDay(SUNDAY); FakeTimeService_SetMinute(1200); LightScheduler_Wakeup(); LONGS_EQUAL(3, FakeLightController_getLastId()); LONGS_EQUAL(LIGHT_ON, FakeLightController_getLastState());}

48Monday, October 18, 2010

Page 9: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

When must you use fakes?

• When the Code under test cannot be fully tested with the real collaborators.

• Examples:– When manual verification is needed (Printed output,

LEDs)– When the results change (Time, random events)– When failures need to be simulated (Network down)– When hardware is involved (LEDs, USB, Sensors,

Motors, IO pins, Flash...)– Operating system calls (RTOS)

49

49Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Breaking Dependencies with Function Pointers

50

50Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Loosening Compile-Time andRun-Time Dependencies

• Problem: an existing C function needs to be stubbed out for some tests...

51

//From the .h file...int RandomMinuteGenerator_Get();...

//From the .c file...int RandomMinuteGenerator_Get(){ ...the implementation... }...

51Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Convert Direct Dependency to Function Pointer

• Modify the function declaration in the header file– Add extern– Add (*)

52

//From the .h file...extern void (*RandomMinuteGenerator_Get)();...

52Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Convert Direct Dependency to Function Pointer

• Modify the function definition in the .c file– Change function name by appending _impl– Hide the _impl by making it static

• Add defining instance of the function pointer and initialize the pointer with the the _impl

53

//From the .c file...static int RandomMinuteGenerator_Get_impl(){ ...the implementation... }

int (*RandomMinuteGenerator_Get)() = RandomMinuteGenerator_Get_impl;

53Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

No Changes are Needed to Callers of the Function Pointer

• Calling code does not change. • No pointer dereferencing is needed

54

//from some production code... ... RandomMinuteGenerator_Get(); ...

54Monday, October 18, 2010

Page 10: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Runtime SubstitutionTest setup and teardown

55

void setup(){ RandomMinuteGenerator_Get = RandomMinuteGenerator_Get_fake;}

void teardown(){ RandomMinuteGenerator_Get = RandomMinuteGenerator_Get_impl;}

• Override the function by assigning the test version of the function during setup

• Don’t forget to restore the original

55Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Use CppUTest’s UT_PTR_SET

56

void setup(){ UT_PTR_SET(RandomMinuteGenerator_Get, RandomMinuteGenerator_Get_fake);}

void teardown(){}

56Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Self Verifying Mock Objects

57

57Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Message Flow for Flash Memory

Block Erase with Error

58

FlashDriver FlashDevice

IOWrite(CommandRegister, 0x40)

IOWrite(offset, data)

IORead(StatusRegister)

b7 == 0IORead(StatusRegister)

b7 == 1, other bits == 0

Repeats while b7 == 0

IORead(offset)

data

Flash_Program(offset, data)

FlashSuccess

58Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Flash Program Flow Chart

59

How do you test these errors in the target?

How do you test these errors in the target?

How Many Tests are Needed?

b7 == 1

Start

Program CommandWrite 0x40 to 0x0

Start/Stop

Write data to address

Read status register

b3 == 0

b4 == 0

b1 == 0

YES

NO

YES

YES

YES

Vpp Error

Program Error

Protected Block Error

NO

NO

NO

Clear status Write 0xFF to 0x0

59Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Mock Object

• Problem - Complex collaborator interactions cannot be captured with a simple spy.

• Solution - Mock Object– A Mock Object is a Test Double that verifies that the code

being tested interacts with its collaborator properly.– The test tells the mock

– The expected calls– In the expected order– What to return.

60

60Monday, October 18, 2010

Page 11: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Flash Driver Test

61

TEST(Flash, ProgramSucceedsReadyImmediately1){ int result = 0; MockIO_Expect_IOWrite(0, 0x40); MockIO_Expect_IOWrite(0x1000, 0xBEEF); MockIO_Expect_IORead(0, 1<<7); MockIO_Expect_IORead(0x1000, 0xBEEF);

result = Flash_Program(0x1000, 0xBEEF);

LONGS_EQUAL(0, result); MockIO_Assert_AllExpectationsMet();}

61Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Mock Flash Read/Write

62

//MockIO.h#include "IOReadWrite.h"

void MockIO_Create(int maxExpectations);void MockIO_Destroy();void MockIO_Expect_IOWrite(ioAddress_t offset, uint16_t data);void MockIO_Expect_IORead(ioAddress_t offset, uint16_t data);void MockIO_Assert_AllExpectationsMet();

//IOReadWrite.h#include <stdint.h>

typedef uint32_t ioAddress_t;typedef uint16_t ioData_t;

ioData_t IORead(ioAddress_t offset );void IOWrite(ioAddress_t offset, ioData_t data);

62Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Mock Flash Read/Write

63

//Snipvoid MockIO_Create(int maxExpectations){ expectations = malloc(sizeof(Expectation)*maxExpectations); memset(expectations, 0, sizeof(expectations)); setExpectationCount = 0; getExpectationCount = 0; maxExpectationCount = maxExpectations; failureAlreadyReported = 0;}

//MockIO.ctypedef struct Expectation{ int kind; ioAddress_t addr; ioData_t value;} Expectation;

static Expectation* expectations = 0;static int setExpectationCount;static int getExpectationCount;static int maxExpectationCount;static int failureAlreadyReported = 0;

63Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Capturing Flash Read/Write Expectations

64

void MockIO_Expect_IOWrite(ioAddress_t addr, ioAddress_t value){ failWhenNoMoreRoomForExpectations(report_too_many_write_expectations); recordExpectation(FLASH_WRITE, addr, value);}

void MockIO_Expect_IORead(ioAddress_t addr, ioAddress_t value){ failWhenNoMoreRoomForExpectations(report_too_many_read_expectations); recordExpectation(FLASH_READ, addr, value);}

64Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Mock Flash Read

65

ioData_t IORead(ioAddress_t addr){ setExpectedAndActual(addr, -1); failWhenNotInitialized(); failWhenNoUnusedExpectations(report_read_but_out_of_expectations); failWhen(expectationIsNot(FLASH_READ), report_expect_write_was_read); failWhen(expectedAddressIsNot(addr), report_read_wrong_address);

return expectations[getExpectationCount++].value;}

65Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Mock Flash Write

66

void IOWrite(ioAddress_t addr, ioData_t value){ setExpectedAndActual(addr, value); failWhenNotInitialized(); failWhenNoUnusedExpectations(report_write_but_out_of_expectations); failWhen(expectationIsNot(FLASH_WRITE), report_expect_read_was_write); failWhen(expectedAddressIsNot(addr), report_write_does_not_match); failWhen(expectedDataIsNot(value), report_write_does_not_match); getExpectationCount++;}

66Monday, October 18, 2010

Page 12: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

All Expectations Must Be Met

67

void MockIO_Assert_AllExpectationsMet(){ if (failureAlreadyReported) return; failWhenNotAllExpectationsUsed();}

67Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Minimize DOH!

Debug

On

Hardware68

68Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

The End

69

69Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

On-line

• Test harnesses– [CPPTEST] www.sourceforge.org, project CppUTest– [FITNESSE] www.fitnesse.org

• Groups• http://groups.yahoo.com/group/testdrivendevelopment• http://groups.yahoo.com/group/AgileEmbedded

70

70Monday, October 18, 2010

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

See Embedded TDD and Related Blogs and Papers

http://www.renaissanceSoftware.net http://www.renaissancesoftware.net/blog/

• Embedded TDD• Zune Bug: Test Driven Bug Fix• Learning Tests are Free! • TDD as a Design Rot Prevention System• Crashing Your Way to Great Legacy C Tests • TDD and the Big Framework Part• Bug Fixes and TDD• Physics of Test Driven Development• Tests vs. Short Term Cache Between Your Ears• Embedded Systems Conference FAQ • I miss constructors • Who says you can’t test drive a device driver?

71

• Why are You Still Using C? • Planing Poker• Agile Embedded Software Development (ESC) • Launching Extreme Programming at a Process

Intensive Company (IEEE) • Test Driven Development for Embedded Software• Progress Before Hardware • Agile Times - Containing Progress Before

Hardware• Test-Driven Development for Embedded C++

Programmers

71Monday, October 18, 2010Test Driven Development

[email protected]

Copyright ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

References and Sources

• [RCM] Robert C. Martin, Clean Code, 2008• [SLAD] Craig Larman and Bas Voode, Scaling Lean & Agile

Development• [WELC] Michael Feathers, Working Effectively with Legacy Code• [REF] Martin Fowler, Refactoring• [TDD] Kent Beck, Test-Driven Development, 2003• [xUNIT] Gerard Meszaros - xUnit Testing Patterns, 2008• [MOCK] Tim Mackinnon and Steve Freeman and Philip Craig, Endo-

Testing: Unit Testing with Mock Objects, 2001• [TD] Lasse Koskela, Test Driven, 2007• [LESSONS] Bret Pettichord Cem Kaner, James Bach. Lessons Learned

in Software Testing: A Context-Driven Approach. John Wiley & Sons, New York, NY, 2002.

72

72Monday, October 18, 2010

Page 13: Test Driven Development for Embedded C€¦ · Test Driven Development james@renaissancesoftware.net Copyright ©2008-2010 James W. Grenning All Rights Reserved. For use by training

Test Driven Developmentwww.renaissancesoftware.net

[email protected] ©2008-2010 James W. GrenningAll Rights Reserved. For use by training attendees.

Presented at Agile China October 14, 2010

Available now in Beta

http://pragprog.com/titles/jgade/

In print early 2011

73

73Monday, October 18, 2010