Integrating White-and Black-Box Techniques for Class-Level ...

5 downloads 127 Views 78KB Size Report
frame call constructors frame return ... Inter-method control flow edges also connect the frame call node of the ... lowing def-use pairs: (a1, g3), (a2, g3), (a3, a6), (a4, s5),. (a6, g12) .... In SEA Software Engineering and Applications Conference.
Integrating White- and Black-Box Techniques for Class-Level Regression Testing Sami Beydeda, Volker Gruhn University of Dortmund Computer Science Department Software Technology 44221 Dortmund, Germany fsami.beydeda, [email protected] Abstract In recent years, several techniques have been proposed for class-level regression testing. Most of these techniques focus either on white- or black-box testing, although an integrated approach can have several benefits. As similar tasks have to be carried out for both white- and black-box testing, an integrated approach can improve efficiency and cost effectiveness. This article explains a new approach for class-level regression testing, integrating existing techniques. Particularly, those of Rothermel et al. and Hong et al. for whitebox regression testing and black-box testing, respectively, have been integrated into a single technique. The benefits of the resulting technique are shown by example.

1 Introduction Testing software to determine whether changes are implemented as intended and that they do not affect other unchanged parts of the software adversely is called regression testing. This article proposes a new technique for regression testing of classes combining two existing techniques. We have integrated the testing techniques of Rothermel et al. [7] and Hong et al. [5]. As the former is a white-box technique and the latter a black-box technique, the resulting technique can be used for simultaneous white- and blackbox testing. Generally, sufficient testing of software requires both white- and black-box techniques. Although both tasks are similar in that they have the same objectives, namely detecting faults within a program, often white- and black-box techniques are applied separately using different tools. The reason is a lack of techniques integrating both strategies. Few approaches have investigated the integration of whiteand black-box testing techniques for object-oriented soft-

ware [4, 1, 2, 3]. Often, the software is tested independently against the specification and source code using different specialized tools. Using different techniques and tools often leads to inefficient test processes. The tester has to work with two different tools which could require different skills and thus separate training. The tester has to be familiar not only with the two different tools, but also with their different underlying concepts. Additionally, using several tools requires higher maintenance effort. Therefore, we think that integrated white- and black-box testing techniques can substantially improve the testing process with respect to both costs and time.

2 Integration of the techniques 2.1 Building blocks One of the building blocks of the proposed technique is the class regression testing technique proposed by Rothermel et al. [7]. The main idea for their technique is to compare two versions of a class to detect and analyze changes. Based on this analysis, test cases covering changed statements are selected from a given test set originating from previous tests. The comparison of the two class versions is carried out using representations called class control flow graphs (CCFGs). A CCFG is a graphical representation of a class visualizing its methods embedded in an abstract test driver. The comparison is carried out by simultaneously traversing both CCFGs and comparing corresponding nodes of the graphs. The other building is the black-box testing technique of Hong et al. [5]. The main idea of this technique is to identify definitions and uses [6] of each attribute and to associate them to each other according to some data flow criteria. After associating definitions and uses with each other, test cases covering these def-use pairs are generated. To ease

t2 t3 t4 t5

test case generation, Hong et al. suggest using a graphical representation of the class, class flow graph (CFG), generated on the basis of a special finite state machine called class state machine (CSM).

inCredit t20

t1

2.2 Demonstrative example

initial t6

The example consists of a class, called account, which simulates a bank account. It provides the appropriate methods for making bank account deposits (deposit()) and withdrawals (withdraw()). Furthermore, it provides methods for paying interest (payInterest()) and for printing bank statements (printBankStatement()). An account, as modeled by an account class, can have three different states, namely inC redit, overdrawn and blocked determined by its balance. Figure 1 shows the specification of class account in form of a CSM. In this figure each state of class account is represented by a circle, while each transition is depicted through an arrow leading from its source state to its target state. These transitions are formally specified through 5-tuples (source; target; event; guard; action) below this figure. A transition consists – besides a source and target state – of an event causing the transition, a predicate guard which has to be fulfilled before the transition can occur, and the action defining the operations on the attributes during the transition. There are also two special circles labeled initial and f inal . These two circles represent the state of the object before its creation and after its destruction, respectively. Thus, they represent states in which the attributes and their values are not defined, meaning that these two states are no concrete states of the object. The source code of class account, built according to the specification in figure 1, is given in figure 2.

2.3 Class specification implementation graph Our method proposed for integrated white- and blackbox testing operates on a graphical representation of the class called class specification implementation graph (CSIG) [3]. The construction of a CSIG consists of three steps: 1. A prototype is constructed for each event type occurring within transitions of the CSM. 2. Then, control flow graphs are generated for each prototype and each method. A CCFG frame is built and the two control flow graphs of each method are inserted into this frame. 3. Def-use pairs for black-box testing are determined on the basis of the CSM and visualized by data flow edges. Each of these steps is described in detail below.

t7

t19

final

t18

t21 overdrawn

t8 t9 t10t11

t22

t14 t12t13

blocked

t15t16t17

= (initial; inC redit; account(); true; balance=0.0;); = (inC redit; inCredit; deposit(amount); true; balance+=amount;); t3 = (inC redit; inCredit; withdraw(amount); balance amount 0; balance-=amount;); t4 = (inC redit; inCredit; payInterest(); true; balance=ic*balance;); t5 = (inC redit; inCredit; printBankStatement(); true; print); t6 = (inC redit; overdrawn; withdraw(amount); limit balance amount < 0; balance-=amount;); t7 = (overdrawn; inCredit; deposit(amount); balance + amount 0; balance+=amount;); t8 = (overdrawn; overdrawn; deposit(amount); balance + amount < 0; balance+=amount;); t9 = (overdrawn; overdrawn; withdraw(amount); limit balance amount < 0; balance-=amount;); t10 = (overdrawn; overdrawn; payInterest(); balance + id1 balance limit; balance+=id1*balance;); t11 = (overdrawn; overdrawn; printBankStatement(); true; print); t12 = (overdrawn; blocked; withdraw(amount); balance amount < limit; balance-=amount;); t13 = (overdrawn; blocked; payInterest(); balance + id1 balance < limit; balance+=id1*balance;); t14 = (blocked; overdrawn; deposit(amount); limit balance + amount < 0; balance+=amount;); t15 = (blocked; blocked; deposit(amount); balance + amount < limit; balance+=amount;); t16 = (blocked; blocked; payInterest(); true; balance+=id2*balance;); t17 = (blocked; blocked; printBankStatement(); true; print); t18 = (inC redit; blocked; withdraw(amount); balance amount < limit; balance-=amount;); t19 = (blocked; inC redit; deposit(amount); balance + amount 0; balance+=amount;); t20 = (inC redit; f inal; finalize(); true; ); t21 = (overdrawn; f inal; finalize(); true; ); t22 = (blocked; f inal; finalize(); true; ): t1

t2















Figure 1. Specification of class account by a class state machine

2.3.1 Constructing a prototype for each event type The construction of a prototype firstly consists of generating a prototype for each transition t = (source; target; event; guard; action) in form of a nested if-then-else construct as shown in figure 3. predicate(source) refers to the predicate of the source state applied to attributes defining the occurrence of state source. For instance, predicate(inCredit) is ‘balance >= 0.0’. After generating these prototypes, those having the same

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

package bank; class transaction { String desc; double amount; double balanceAfter; transaction(String desc_, double amount_, double balanceAfter_) { desc = desc_; amount = amount_; balanceAfter = balanceAfter_; } } class account { private double balance; private double limit; private transaction[] t; private int idx; private double ic, id1, id2; public account() { balance = 0.0; limit = -500.0; t = new transaction[20]; idx = 0; ic = 0.05; id1 = 0.1; id2 = 0.2; } public void deposit(double amount) { balance += amount; t[idx++] = new transaction("Deposit ", amount, balance); }

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 }

public void withdraw(double amount) { if (balance >= limit) balance -= amount; t[idx++] = new transaction("Withdraw", amount, balance); } public void payInterest() { double amount; if (balance >= 0.0) { amount = ic*balance; balance = amount; } else if (balance >= limit) { amount = id1*balance; balance += amount; } else { amount = id2*balance; balance += amount; } t[idx++] = new transaction("Interest", balance, amount); } public void printBankStatement() { System.out.println("Type\t\tAmount\tBalance"); for (int j = 0; j < idx; j++) System.out.println(t[j].desc + "\t" + t[j].amount + "\t" + t[j].balanceAfter); }

Figure 2. Java source code of class account if (predicate(source)) // state if (guard) // guard action; // action else throw new ErrorStateException(); else throw new ErrorStateException();

Figure 3. Prototype of a transition depositSpec(amount) { if (predicate(inCredit)) if (true) balance += amount; // method: deposit() else throw new ErrorStateException(); else if (predicate(overdrawn)) if (balance + amount >= 0.0) balance += amount; // method: deposit() else if (balance + amount < 0) balance += amount; // method: deposit() else throw new ErrorStateException(); else if (predicate(blocked)) if (limit