Test d’intégration - Inriapeople.rennes.inria.fr/Benoit.Baudry/slides/vv/5-integration.pdf ·...

Preview:

Citation preview

Test d’intégration

- 1 -

Plan

1.  Introduction au test d’intégration

2.  Mise en œuvre des mock avec EasyMock

- 2 -

Plan

1.  Introduction au test d’intégration

2.  Mise en œuvre des mock avec EasyMock

- 3 -

Intégration

• But : tester les interactions entre classes • Lien entre test d’intégration et unitaire: •  il faut ordonner les classes pour le test

• Il faut identifier les dépendances entre classes • Problème dans le cas de cycles de dépendances

- 4 -

Cas simple : un graphe acyclique

- 5 -

Ordre partiel pour le test: F, (C, D), E, B, A

A

B E

D C

F Graphe de

dépendances acyclique

A B

C

D

E

F

- b

*

- e

1

- d 0..1

- d

0..1

- f

*

- f 0..1

Étape 1

- 6 -

A B

C

D

E

F

testF

- b

*

- e

1

- d 0..1

- f

*

- f 0..1

- d

0..1

- f

1

Étape 2

- 7 -

A B

C

D

E

F

testFtestC

testD

- b

*

- e

1

- d 0..1

- f

*

- f 0..1

- d

0..1

- f

1

- c1

- d

1

Étape 3

- 8 -

A B

C

D

E

F

testFtestC

testD

testE - b

*

- e

1

- d 0..1

- f

*

- f 0..1

- d

0..1

- f

1

- c1

- d

1

- e

1

Étape 4

- 9 -

A B

C

D

E

F

testFtestC

testD

testE

testB

- b

*

- e

1

- d 0..1

- f

*

- f 0..1

- d

0..1

- f

1

- c1

- d

1

- e

1

- b1

Étape 5

- 10 -

A B

C

D

E

F

testFtestC

testD

testE

testBtestA

- b

*

- e

1

- d 0..1

- f

*

- f 0..1

- d

0..1

- f

1

- c1

- d

1

- e

1

- b1 - a1

Cas moins simple: présence de cycles

- 11 -

Display Bank

Person

Account Operation

DOp WOp

- balance : floa t- number: int- overdraft: float

+ deposit( )+ wit hdraw()+ Account()

Account- account Numbers: int

+ Ba nk()+ addAccount()+ closeAccount()+ deposit( )+ getAccount()+ getClient()+ wit hdraw()+ getAccountsOfClient( )

Bank

- amount: f loat

+ Operation()+ getOperationType()

Operation+ displayAccountsOfClie nt()+ process( )+ readInt()+ menu()+ readNumber()+ closeAccount()+ wit hdraw()+ history( )+ clie nts()

Display

- name: Str in g

+ Pe rson()

Person + DepositOperation()+ getOperationType()

DepositOperation

+ WithdrawOperation()+ getOperationType()

WithdrawOperation - accoun ts*

+ owner0..1

- history

*

- accoun ts

*

- clients

*

- display - bank

1

Intégration avec cycles

• Il faut casser les cycles • développer des simulateurs de classes (« bouchon de

test » ou « stub »)

• un simulateur a la même interface que la classe simulée, mais a un comportement contrôlé

• Exemple

- 12 -

Exemples de stub

- 13 -

/** * Creates an account for the person named name * If no client has this name, a new client object is created and is added to the list of clients, then the account is created * If the client exists the account is created, added to the bank's and the client's list of accounts */ public int addAccount(String name, float amount, float overdraft) { this.accountNumbers++; Person p = getClient(name); //if a client named name already exists in the bank's set of clients if (p!=null){ Account a = new Account(p, amount, overdraft, accountNumbers); p.addAccounts(a); this.addAccounts(a); } //if the client does not exist, add it tp the bank's list of clients and create account else{ Person client = new Person(name); this.addClients(client); Account a = new Account(client, amount, overdraft, accountNumbers); client.addAccounts(a); this.addAccounts(a); } return accountNumbers; }

Exemples de stub

- 14 -

Stub 1

/** * Creates an account for the person named name * If no client has this name, a new client object is created and is * added to the list of clients, then the account is created * If the client exists the account is created, added to the bank's and the client's list of accounts */ public int addAccount(String name, float amount, float overdraft) { return 1; }

Stub 2

/** * Creates an account for the person named name * If no client has this name, a new client object is created and is * added to the list of clients, then the account is created * If the client exists the account is created, added to the bank's and the client's list of accounts */ public int addAccount(String name, float amount, float overdraft) { return 10000000; }

Exemples de stub

- 15 -

/** * Looks for a person named name in the set of clients. * Returns the Person object corresponding to the client if it exists * Returns null if there is no client named name */ public Person getClient(String name) { Iterator it = this.clientsIterator(); while (it.hasNext()){ Person p = (Person)it.next(); if(p.getName()==name){ return p; } } return null; }

Exemples de stub

- 16 -

Stub 1

/** * Looks for a person named name in the set of clients. * Returns the Person object corresponding to the client if it exists * Returns null if there is no client named name */ public Person getClient(String name) { return null; }

Stub 2

/** * Looks for a person named name in the set of clients. * Returns the Person object corresponding to the client if it exists * Returns null if there is no client named name */ public Person getClient(String name) { return new Person(“toto”); }

Exemple Banque

• Exemple, pour tester en présence de ce cycle

- 17 -

Person

Account

public class Person { /* * Initializes the name of the person with the param n * Creates a new vector to intialize the acounts set */ public Person(String n){ name = n; accounts = new Vector(); }

public String getName(){return name;} }

Stub de la classe Person public class Person { /* * Initializes the name of the person with the param n * Creates a new vector to initialize the accounts set */ public Person(String n){ } public String getName(){return (“toto”);} }

Regarder quelles sont les méthodes de Person utilisées par Account

Exemple Banque

• Etape 1 • Tester la classe Account avec le stub de Person

• Etape 2 • Tester la classe Person avec Account

• Etape 3 • Retester la classe Account avec la vraie classe Person

- 18 -

Retr. listSummary

StatusMa ster/SlLSReq list

Ne ighbor

NME LME GARME

L3_SDU

IADU GADUN MM

H eader

Serv type

D st addrSrc addr

Length.........

L3_Payload Pad

L3_DTPDU

DTE

ISSI Link Set

Link ID

UpMe tricsMe th.Updt

Key_Space

L_ID

Line

CapacityRelativeDKey_Range

Key

Current CL

Previous CLPa ram. smoothSampleTime

W T M

CME

(durations)

CMPDU

CMP

ECN

DeadIntHelloInt

RxmtIntMaxRetrTime

InfTransDelay

MinLSIntLSRe freshTime

MaxA geMaxA geDiff

RME

RMPDU

LSta te

HeaderLSA

Encapsulate

Read and Write

with

with

com

.

com

.

com

.w

ith

Hello

Coope rate for routing

Store a

nd upd

at e

made of

consult

compu

te

(c omparaison)

sel ect

Read and Modify

com

m uni

cate

send s

pecia

l ECNSt or e an d up d at e

com

.wi

th

1..*

2*

Mana gerSystemOperator

S up ervise

Entity

Clockconsult

withCommunicate

Supe

r vise

NeighborCongested

PUD countDP NeighborCL Neighbor

manage1..*

Cooperate for congestion

Read

Tabl

eRo

utin

g Path TypeAdvertiser

Root

Next Hops

Table

Floading

Prev. Hop

has arepresentation

RM_Data

RMP_Hea derN

ext Hop

Exch. Nb

Subsc riber

com

unic

ate t

hrou

gh

has l

ocal

comunic ate through comm unic ate with local

GroupGroupID

Made of

Traffic Simulator

Profile

1

2 3 4 5 6

7 8 9

10

11

12

13 14 1516

17 18

1920 21 22

23

24 2526

2728

29

30 31

3233

34

35 36

37

Cas encore moins simple

- 19 -

37 classes

Cas encore moins simple

• Contraintes sur la conception • pas d’interdépendances

• contrainte forte dans un cadre OO

• Sans contraintes sur la conception • on intègre tout d’un coup: stratégie « big bang »

• heuristique pour prendre en compte les interdépendances au moment de l’intégration

- 20 -

Une stratégie efficace pour l’ordre d’intégration

• Basée sur un modèle de graphe: graphe de dépendances de test (GDT) • Deux types de dépendances • héritage • client/serveur

• Dépendances • classe – classe • méthode – classe

- 21 -

Transformation UML vers GDT

22

2 types de noeuds –  classe – méthode

A A

A … mA1(…) ...

A mA1

Classe A Noeud classe

Méthode mA1 de la classe A

Noeud méthode dans une classe

Transformation UML vers GDT

23

3 types d’arcs –  class_to_class – method_to_class – method_to_method

A B A dépend de B

mA1

mA2

B

mA1

Transformation UML vers GDT

24

Méthode vers classe A ... +mA1(...v1: B...) … mA2(…v: A…) ...

A B

Méthode vers méthode A +mA1(...v: B...) {… v.mB1 …} +mA2(...v: A...) {… v.mA1 …}

A B

Langage d’action (AS / OCL ...)

raffinement

mA1

mA2 mB1

mA1 mA2

Transformation UML vers GDT

25

A

B

association

A

B

composition

A

B aggregation

A

B association unidirectionnelle

dépendance

A

B

A

C

B

classe d’association

A

B

Interface

Interface Name A

B héritage

A C B

A

B

A

B

Transformation UML vers GDT

26

B +mB1(v1: C) … -pB1(v: C) -pB2(v: G) …. #redefine pA1 {…}

D - pD1(v1:E, v2 : F)

E

F C

H

G

+mA1(v1: C) {….} #pA1(…) {…}

A

F

E

B

G H pA1 mB1

pB1 pB2

A

C

D

mA1

pD1

pA1

Transformation UML vers GDT

27

F

E

B

G H

pA1 mB1 pB1

pB2

A C

D

mA1

pD1

pA1

C

D A

mA1

BmB1

Supprimer les dépendances spécifiques à l’implantation

Transformation UML vers GDT

28

mB1 mB2

mB3

GDT préliminaire

A

mA1 mA2

B Pas un graphe classique

solution 1

A B

Perte d’information

A mA1

mA2

B

mB1 mB2 mB3

solution 2

Pas de perte d’information homomorphisme

Une stratégie efficace pour l’ordre d’intégration

• Comment choisir un ordre d’intégration à partir du GDT? • Minimiser le nombre de stubs à écrire

• stub réaliste => simule tous les comportements (réutiliser une ancienne version du composant)

29

C’ simule le comportement de C vis-à-vis de A et B

A dépend de C’ C

B

Une stratégie efficace pour l’ordre d’intégration

• Stub spécifique => ne simule que les comportements utilisés par le client

30

A dépend de C

B

A dépend de C’ stub

B dépend de C’’ stub

un stub spécifique de C pour A et un autre pour B

Une stratégie efficace pour l’ordre d’intégration

31

a

b

k

f

d

c

i

j

g

l

h

e

ordre optimal => NP-complet complexité = n!

Une stratégie efficace pour l’ordre d’intégration

32

a

b

k

f

d

c

i

j

g

l

h

e

a

b

k

f

d

c

i

j

g

l

h

e

A C

B

Tarjan

Identifie les composantes fortement connexes => ordre partiel initial [(e ou C), (A ou B), a] Complexité linéaire avec le nombre de noeuds

Une stratégie efficace pour l’ordre d’intégration

33

f

i

j

g

h B

Algorithme de Bourdoncle

Noeud candidat = # max(fronds)

f

i

j g h

[(e ou C), (A ou B), a] Casse les CFCs réapplique Tarjan éventuellement [l,k] [c,b,d] [g,h,i,j,f]

Une stratégie efficace pour l’ordre d’intégration

34

a

b

e k

f d

c

i

j

g

h

l

Algorithme optimisé #stubs spécifiques = 4 #stubs réalistes = 3

Résultat = un ordre partiel de toutes les stratégies possibles

Génération aléatoire (moy.) #stubs spécifiques = 9.9 #stubs réalistes = 5

Exo

• Plan de test d’intégration pour :

35

B

E

F

H G

A

C

D

I J

Cas encore moins simple

36

37 classes

Retr. listSummary

StatusMa ster/SlLSReq list

Ne ighbor

NME LME GARME

L3_SDU

IADU GADUN MM

H eader

Serv type

D st addrSrc addr

Length.........

L3_Payload Pad

L3_DTPDU

DTE

ISSI Link Set

Link ID

UpMe tricsMe th.Updt

Key_Space

L_ID

Line

CapacityRelativeDKey_Range

Key

Current CL

Previous CLPa ram. smoothSampleTime

W T M

CME

(durations)

CMPDU

CMP

ECN

DeadIntHelloInt

RxmtIntMaxRetrTime

InfTransDelay

MinLSIntLSRe freshTime

MaxA geMaxA geDiff

RME

RMPDU

LSta te

HeaderLSA

Encapsulate

Read and Write

with

with

com

.

com

.

com

.w

ith

Hello

Coope rate for routing

Store a

nd upd

at e

made of

consult

compu

te

(c omparaison)

sel ect

Read and Modify

com

m uni

cate

send s

pecia

l ECNSt or e an d up d at e

com

.wi

th

1..*

2*

Mana gerSystemOperator

S up ervise

Entity

Clockconsult

withCommunicate

Supe

r vise

NeighborCongested

PUD countDP NeighborCL Neighbor

manage1..*

Cooperate for congestion

Read

Tabl

eRo

utin

g Path TypeAdvertiser

Root

Next Hops

Table

Floading

Prev. Hop

has arepresentation

RM_Data

RMP_Hea derN

ext Hop

Exch. Nb

Subsc riber

com

unic

ate t

hrou

gh

has l

ocal

comunic ate through comm unic ate with local

GroupGroupID

Made of

Traffic Simulator

Profile

1

2 3 4 5 6

7 8 9

10

11

12

13 14 1516

17 18

1920 21 22

23

24 2526

2728

29

30 31

3233

34

35 36

37

Graphe de dépendances

37

quelques cycles...

Bouchons de test

38

stubs réalistes

Une stratégie efficace pour l’ordre d’intégration

• Quand un ordre partiel est disponible, on peut paralléliser les tâches • en fonction d’un nombre fixe de testeurs

• pour un délai minimum

39

Répartition des testeurs • 1 testeur : Les feuilles d’abord. • n testeurs (n > 1)

ü Hypothèse •  Le temps de test de toutes les unités •  est identique

ü Exemple n = 2 • Un ordre possible :(B, F), (D, G), E, C, A • Un ordre optimal :

•  (G, F), (B, E), (C, D), A •  (B, G), (E, F), (C, D), A

40

A

D C

E F

G

B

Répartition des testeurs

• Propriété: min_étapes = max (A,B) A = plus long chemin

B = (nb_noeuds / nb_testeurs) + 1

• Exemple • 4 testeurs et 37 noeuds;

•  plus long chemin = 8 • B = (37 div 4) +1 = 10

• min_étapes = 10 41

Répartition des testeurs • Exemple de GNU-Eiffel

• Nombre de testeurs nécessaire pour intégrer en un minimum de temps •  88 div 7 +1 = 13 testers

•  temps minimum : 7 étapes

42

1 2

3 4 5

7

6

Répartition des testeurs

1

2

3

4

5

6

7

54 4 5 26 25 2 3 18 19 37 55 56 16

17 65 22 33 38 15 23 48 49 51 52 11 36

28 32 85 78 47 53 21 34 9 1 13 20 10

79 82 45 6 7 8 42 60 31 10 27 57 66

71 81 67 74 75 76 77 29 39 40 46 61 44

59 83 84 86 87 88 58 68 62 63 50 12 14

64 43 30 41 69 70 24 72 73 80

Component/node Step

43

7 steps =

Optimum delay

Répartition des testeurs

• Branches : Ø A-B

Ø A-C-E-G

Ø A-D-F

• Chemin critique Ø A- C-E-G

• Répartition totale #testeurs = #branches

temps_de_test = temps_de_test_de_chemin_critique 44

A

D C

E F

G

B

Répartition des testeurs

• Condition : #testeurs < #branches

• Répartition : Ø Choisir la feuille d’un chemin critique. Ø Exemple avec 2 testeurs :

•  1er : G E C A •  2nd : F B D

• Avantages : Ø Profiter du temps libre d’un testeur sur les branches non-

critiques 45

A

D C

E F

G

B

Répartition des testeurs

46

Une solution optimale pour l’exemple du SMDS

37 nœuds, 4 testeurs ⇒ (minimum = 10)

Une stratégie efficace pour l’ordre d’intégration

• Stratégie efficace • casse les cycles de dépendances avec un minimum

de stubs

• Autre stratégie • prend en compte les pratique de conception OO

• certains cycles sont très cohérents du point de vue fonctionnel (Ex: design patterns) •  ça peut être intéressant d’intégrer cette interdépendance

d’un coup 47

Stratégie mixte

• Minimise encore le nombre de stubs • Maintient un niveau de cohérence dans l’intégration • Pas complètement automatisable • pattern matching

48

sous-système qui peut être intégré en “big bang”

Reste du système

MAILER

-ClosedSate closed;-ConnectingState connecting;-ConnectedState connected;-*string pendingQueue

+Mailer(){ currentState = closed}+void send(string mess){ currentState .send(mess)}+void steState(MialerState s){ currentState = s;}+string getState()+*string getPendingQueue()…

MAILERSTATE

+MailerState(Mailer m){ mailer = m;}+send(string m)

-currentState-mailer

CLOSEDSTATE

+send(string m){ print(“Trying to send while closed : connecting”); mailer.set_pending_message(mess); mailer.setState(connecting);}+getMailer()

CONNECTINGSTATE

+send(string m){ print(“Trying to send while connecting”); mailer.set_pending_message(mess);}

CONNECTEDSTATE

+send(string m){ print(« Effective send while connected »); send_message(mess);}-send_message(string m)

Closed Connecting

Connected

send

open

close

send

send

notify_connect

init

Plan

1.  Introduction au test d’intégration

2.  Mise en œuvre des mock avec EasyMock

- 49 -

Mocks

• Les mocks sont un type de stub • Simulent les échanges de message entre une classe et ses dépendances • Permet de tester • une classe en isolation

• Les interactions avec l’environnement

- 50 -

Utilisation •  Étapes:

1.  Création du mock

2.  Configuration du mock pour lui dire comment il doit se comporter quand il est appelé.

3.  Activation des mocks (C’est appelé la fonction “replay” dans la documentation d’EasyMock)

4.  Exécuter les tests

5.  Après l’exécution, vérifier le mock pour savoir si les appels attendus ont eu lieu

- 51 -

Mockito

• Mockito is used by calling static methods defined in the `org.mockito.Mockito` class. •  To create a mock: MyClass someMock = mock(MyClass.class);!

• To configure a mock: !when(someMock.someOp()).thenReturn(« someResult »);!

• Tip •  import static org.mockito.Mockito.*; to write mock()

instead of Mockito.mock(...)!

52

Unit test Client

53

Client - name: String [1]

+ Client( userName: String, service: Service): Client + useService(): Integer + getUserName(): String

«Interface»Service

+ service(): Integer

useService calls "service" and returns the obtained value increased by 10

+ service

[0..1]

Stub Service class

54

import org.junit.Test;!import static org.mockito.Mockito.*;!import static org.junit.Assert.*;!...!!public class ClientTestWithStub {!

!!!@Test!!public void testUseService() {!! !!! !// Preparing the context -- we stub the Service instance!! !Service mockService = mock(Service.class);!! !when(mockService.service()).thenReturn(1327);!! !Client client = new Client("John",mockService);!! !!! !// Calling the tested operation!! !int result = client.useService();!! !!! !// Oracle!! !assertEquals(result,1337);!! !!!}!

}!

Checking calls on methods

55

B

+ b()

A

+ A( b1: B, b2: B): A + a()

+ b1 [1]

+ b2 [1]

sd: A behavior

a:A b1:B b2:B

a()

b()

b()

a()

b()

b()

Checking calls on methods

• It is also possible to check chat methods are called on a mock

• A different form of oracle 56

// Context!B mockB1 = mock(B.class);!B mockB2 = mock(B.class);!A someA = new A(mockB1,mockB2);!// Calling the tested operation!a.a();!// Checking if b1.b() was called during a()!// to see if b1 was used correctly by a!verify(b1).b();!

Checking calls on methods

• It is also possible to check the order of method calls

57

// Checking if b1.b() was called before b2.b()!InOrder mocksWithOrder = inOrder(mockB1, mockB2);!mocksWithOrder.verify(b1).b();!mocksWithOrder.verify(b2).b();!

Test the behavior of a wrapper for Service!

58

RealService

+ RealService(): RealService + service(): Integer

Client - name: String [1]

+ Client( userName: String, service: Service): Client + useService(): Integer + getUserName(): String

«Interface»Service

+ service(): Integer

SecurityWrapper

+ SecurityWrapper( realService: RealService, client: Client, authChecker: AuthChecker): SecurityWrapper + service(): Integer

AuthChecker

+ AuthChecker() + checkRights( userName: String): Boolean

useService calls "service" and returns the obtained value increased by 10

+ service

[0..1]

- realService

[0..1]

- client [0..1]

- authChecker [0..1]

Test the behavior of a wrapper for Service

59

sd: SecurityWrapper behavior

alt[authOK=true]

:Client realService:RealServiceservice:SecurityWrapper authChecker:AuthChecker

checkRights(userName: String): Boolean

service(): Integer

getUserName(): String

result

useService(): Integer

username

if (authOk) then result else -1

service(): Integer

authOk

useService(): Integer

getUserName(): String

checkRights(userName: String): Boolean

username

authOk

service(): Integer

result

if (authOk) then result else -1

service(): Integer

Recommended