test d’intégration - inriapeople.rennes.inria.fr/benoit.baudry/slides/vv/5-integration.pdf ·...
TRANSCRIPT
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