Object Oriented Testing

Published:

This essay has been submitted by a student. This is not an example of the work written by our professional essay writers.

Chapter 9

Object Oriented Testing

What is object orientation? Why is it becoming important and relevant in software development? How is it improving the life of software developers? Is it a buzzword? Many such questions come into our mind whenever we think about object orientation of software engineering. Companies are releasing the object oriented versions of existing software products. Customers are also expecting object oriented software solutions. Many developers are of the view that structure programming, modular design concepts, conventional development approaches are old fashioned activities and may not be able to handle today's challenges. They may also feel that real world situations are effectively handled by object oriented concepts using modeling in order to understand them clearly. Object oriented modeling may improve the quality of the SRS document, the SDD document and may help us to produce good quality maintainable software. The software developed using object orientation may require different set of testing techniques, although, few existing concepts may also be applicable with some modifications.

9.1 What is Object Orientation?

We may model real world situations using object oriented concepts. Suppose, we want to send a book to our teacher who does not stay in the same city. We cannot go to his house for delivery of the book because he stays in a city which is 500 km away from our city. As we all know, sending a book is not a difficult task. We may go to a nearby courier agent (say fast track courier) and ask to deliver the book to our teacher on his address. After this, we are sure that the book will be delivered automatically and also within specified time (say two days). The agents of fast track courier will perform the job without disturbing us at all.

Our objective is that we want to send a book to our teacher who does not stay in our city. This objective may be simply achieved, when we identify a proper “agent” (say fast track courier) and give a “message” to send the book on an address of the teacher. It is the “responsibility” of the identified agent (fast track agent) to send the book. There are many “methods” or ways to perform this task. We are not required to know the details of the operations to be performed to complete this task. Our interest is very limited and focused. If, we investigate further, we may come to know that there are many ways to send a book like using train network, bus network, air network or combinations of two or more available networks. The selection of any method is the prerogative of our agent (say agent of fast track company). The agent may transfer the book to another agent with delivery address and a message to transfer to next agent and so on. Our task may be done by a sequence of requests from one agent to another.

In object orientation, an action is initiated by sending a message (request) to an agent who is responsible for that action. An agent acts as a receiver and if it accepts a message (request), it becomes its responsibility to initiate the desired action using some method to complete the task.

In real world situations, we do not need to know all operations of our agents to complete the assigned task. This concept of “information hiding” with respect to message passing has become very popular in object oriented modeling. Another dimension is the interpretation of the message by the receiver. All actions are dependent upon the interpretation of the received message. Different receivers may interpret same message differently. They may decide to use different methods for the same message. Fast track agency may use air network while another agency may use train network and so on. If we request our tailor to send the book, he may not have any solution for our problem. The tailor will only deny such requests. Hence, a message should be issued to a proper agent (receiver) in order to complete the task. Object orientation is centered around few basic concepts like objects, classes, messages, interfaces, inheritance and polymorphism. These concepts help us to model a real world situation which provides foundation for object oriented software engineering.

9.1.1 Classes and Objects

We consider the same example of sending a book to a teacher. The selection of the courier company is based on its reputation and proximity to our house. A courier management system is required in order to send a book to a teacher. All courier types (such as book, pen, etc.) may be combined to form a group and this group is known as a class. All objects are instances of a class. The class describes the structure of the instances which include behaviour and information. In our example, courier (containing courier details) is a class and all courier types send are its objects as shown in 9.1.

All objects have unique identification and are distinguishable. There may be four horses of same colour, breed and size but all are distinguishable due to their color. The term identity means that objects are distinguished by their inherent existence and not by descriptive properties [JOSH03].

What types of things become objects? Anything and everything may become an object. In our example, customer, courier, tracking are nothing but objects. Class of an object provides the structure for the object i.e. its state and operations. A courier class is shown in 9.2 with eight attributes and four operations.

Courier

Description

Weight

Length

Width

Height

Cost

DeliveryStatus

Address

addcourierdetail()

deletecourier()

updatecourier()

viewstatus()

Name

Attributes / State / Information

Operations / Behaviour

An attribute (or information / state) is a data value held by the object of a class. The courier may have different height, weight, width, length, description and it may be delivered or not. The attributes are shown as second part of the class courier as given in 9.2. Operations (or behaviour) are the functions which may be applied on a class. All objects of a class have same operations. A class courier shown in 9.2 has four operations namely ‘addcourierdetail', ‘deletecourier', ‘updatecourier' and ‘viewstatus' are four operations defined for a Class Courier in the 9.2. In short, every object has list of functions (operation part) and data values required to store information (Attribute part).

I. Jacobson has defined a class as [JACO98]:

“A class represents a template for several objects and describes how these objects are structured internally. Objects of the same class have the same definition both for their operations and for their information structures”.

In object oriented system, every object has a class and object is called instance of that class. We use object and instance as synonyms and object is defined as [JACO98]:

“An instance is an object created from a class. The class describes the (behaviour and information) structure of the instance, while the current state of the instance is defined by the operations performed on the instance”.

9.1.2 Inheritance

We may have more information about Fast Track Courier Company not necessarily because it is a courier company but because it is a company. As a company, it will have employees, balance sheet, profit / loss account and a chief executive officer. It will also charge for its services and products from the customers. These things are also true for transport companies, automobile companies, aircraft companies etc. Since the category courier company is more specialized form of the category company and any knowledge of a company is also true for a courier company and subsequently also true for Fast Track Courier Company.

Fast track courier is a specialized category of a courier company, however, courier company is a specialized category of a company. Moreover, a nation may have many companies and around the globe, we have many nations, all knowledge gathered so far, may not be directly applicable to fast track company that knowledge of a more general category is also applicable to a specialized category is called inheritance. We may say that the category fast track courier will inherit attributes of the category courier company and courier company will inherit the attributes of a category company. This category is nothing but the class in the object oriented system. There is another tree like structure is used to represent hierarchy of classes and is shown in 9.4.

Information of a courier company is applicable to Fast Track Company because it is a sub class of class Courier Company. Same information of a courier company is also applicable to Air World Courier and Express Courier because they are also sub classes of the class courier company. All classes inherit information from the upper classes. Hence information from a base class is common to all the derived classes, however, each derived class also has some additional information of its own. Each derived class inherits the attributes of its base class and this process is known as inheritance. In general, low level classes (known as subclasses or derived classes) inherit state and behaviour from their high level class (known as a super class or base class).

9.1.3 Messages, Methods, Responsibility, Abstraction

Objects communicate through passing messages. A message is a request for performing an operation by some object in the system. A message may consist of the identification of the target object, name of the requested operation and other relevant information for processing the request. An object which originates a message is called the sender and the object which receives a message is called the receiver. An object may send a message to other object or even to itself to perform designed functions. A ‘method' is the sequence of steps (or set of operations) to be performed to fulfill the assigned task. There may be many methods available for any task. It is the responsibility of receiver of the message to choose an appropriate method to complete task effectively and efficiently. In 9.2, four methods ‘addcourierdetail', ‘deletecourier', ‘updatecourier' and ‘viewstatus' are available for courier class. In order to retrieve the delivery status of the courier, ‘viewstatus' method must be invoked.

Responsibility is an important concept of object oriented system. Behaviour of an object is described in terms of responsibilities. Fast track courier is free to use any method to send the book without our involvement and interference. This type of independence increases the level of abstraction. This improves the independence amongst the objects which is very important for solving any complex problem. The complexity of a problem is managed using right level of abstraction which is the elimination of irrelevant and the amplification of the essentials. We learn driving a car by knowing driving essentials like steering wheel, ignition, clutch, break, gear system without knowing any details of type of engine, batteries, control system etc. These details may not be required for a learner and may create unnecessary confusion. Hence, abstraction concept provides independence and improves the clarity of the system.

9.1.4 Polymorphism

The dictionary meaning of polymorphism is “many forms”. In the real world, the same operations may have different meanings in different situations. For example, “Human” is a subclass of “Mammal”. Similarly “Dog”, “Bird”, “Horse” are also sub classes of “Mammal”. If a message “come fast” is issued to all mammals, all may not behave in the same way. Horse and dog may run, bird may fly and human may take an aircraft. The behaviour of mammals is different on the same message. This concept is known as polymorphism, where the same message is sent to different objects irrespective of their class, but the responses of objects may be different. When we abstract the interface of an operation and leave the implementation details to subclasses, this activity is called polymorphism. This operation is called polymorphic operation. We may create a super class by pulling out important state, behaviour and interfaces of the classes. This may further simplify the complexity of a problem. An object may not need to know the class of another object to whom it wish to send a message, when we have polymorphism. This may be defined as [JACO98]:

“Polymorphism means that the sender of a stimulus (message) does not need to know the receiving instance's class. The receiving instance can belong to an arbitrary class”.

Polymorphism is considered to be an important concept of any object oriented programming language. As we all know, arithmetic operators such as +, =, - are used to operate on primary data types such as int, float etc. We may overload these operators so that they may operate in the same way on objects (user defined data types) as they operate on primary data types. Thus, the same operators will have multiple forms.

9.1.5 Encapsulation

Encapsulation is also known as information hiding concept. It is a way in which both data and functions (or operations) that operate on data are combined into a single unit. The only way to access the data is through functions, which operate on the data. The data is hidden from the external world. Hence, it is safe from outside (external) and accidental modifications. For example, any object will have attributes (data) and operations which operates on the specified data only.

If data of any object needs to be modified, it may be done through the specified functions only. The process of encapsulating the data and functions into a single unit simplifies the activities of writing, modifying, debugging and maintaining the program.

In a university, every school may access and maintain its data on its own. One school is not allowed to access the data of other school directly. This is possible only by sending a request to other school for the data. Hence, the data and functions that operate on the data are specific to each school and are encapsulated into a single unit which is the school of a university.

9.2 What is Object Oriented Testing?

Object oriented programming concepts are different from conventional programming and have become preferred choice for large scale system design. Fundamental entity is the class that provides an excellent structuring mechanism. It allows us to divide a system into well defined units which may then be implemented separately. We still do unit testing although the meaning of unit has changed. We also do integration and system testing to test the correctness of implementation. We also do regression testing in order to ensure that changes have been implemented correctly. Although many concepts and techniques are different from conventional testing.

9.2.1 What is a Unit?

In conventional programming, a unit is the smallest portion of the program that can be compiled and executed. We may call it a module, component, function or procedure. In object oriented system, we have two options for a unit. We may treat each class as a unit or may treat each method within a class as a unit. If a class is tested thoroughly, it can be reused without being unit tested again. Unit testing of a class with a super class may be impossible to do without the superclasses methods/variables. One of the solution is to merge the superclass and the class under test so that all methods and variables are available. This may solve immediate testing problem and is called flattening of classes. But classes would not be flattened in the final product, so potential issues may still prevail. We may have to redo flattening after completion when dealing with multiple inheritance. If we decide to choose method as a unit, then these issues will be more complicated and difficult to implement. Generally, classes are selected as a unit for the purpose of unit testing.

9.2.2 Levels of Testing

We may have 3 or 4 levels of testing dependening on our approach. The various testing levels are:

* Method testing (Unit testing)

* Class testing (Unit testing)

* Interclass testing (Integration testing)

* System testing

In order to test a class, we may create an instance of the class i.e. object and pass the appropriate parameters to the constructor. We may further call the methods of the object passing parameters and receive the results. We should also examine the internal data of the object. The encapsulation plays an important role in class testing because data and function (operations) are combined in a class. We concentrate on each encapsulated class during unit testing but each function may be difficult to test independently. Interclass testing considers the parameter passing issues between two classes and is similar to the integration testing. System testing considers the whole system and test cases are generated using functional testing techniques.

Integration testing in object oriented system is also called interclass testing. We do not have hierarchical control structure in object orientation and thus conventional integration testing techniques like top down, bottom up and sandwitch integration can not be applied. There are three popular techniques for interclass testing in object oriented systems. The first is the thread based testing where we integrate classes that are needed to respond to an input given to the system. Whenever we give input to a system one or more classes are required to be executed that respond to that input to get the result. We combine such classes which execute together for a particular input or set of inputs and this is treated as a thread. We may have many threads in a system, depending on various inputs. Thread based testing is easy to implement and has proved as an effective testing technique. The second is the use based testing where we combine classes that are required by one use case.

The third is the cluster testing where we combine classes that are required to demonstrate one collaboration. In all three approaches, we combine classes on the basis of a concept and execute them to see the outcome. Thread based testing is more popular due to its simplicity and easy implementability.

The advantage of object oriented system is that the test cases can be generated earlier in the process, even when the SRS document is being designed. Early generation of test cases may help the designers to better understand and express requirements and to ensure that specified requirements are testable. Use cases are used to generate good number of test cases. This process is very effective and also saves time and effort. Developers and testers understand requirements clearly and may design an effective and stable system. We may also generate test cases from the SDD document. Both the teams (testers and developers) may review the SRS and the SDD documents thoroughly in order to detect many errors before coding. However, the source code testing is still very important part of testing and all generated test cases will be used to show their usefulness and effectiveness. We may also generate test cases on the basis of the availability of the source code.

Path testing, state based testing and class testing are popular object oriented testing techniques and are discussed in subsequent sections.

9.3 Path Testing

As discussed earlier, path testing is a structural testing technique where the source code is required for the generation of test cases. In object oriented testing, we also identify paths from the source code and write test cases for the execution of such paths. Most of the concepts of conventional testing such as generating test cases from independent paths are also applicable in object oriented testing.

9.3.1 Activity Diagram

The first step of path testing is to convert source code into its activity diagram. In unified modeling language (UML), activity diagram is used to represent sequences in which all activities are performed. This is similar to a flow graph which is the basis of conventional path testing. Activity diagram may be generated from a use case or from a class. It may represent basic flow and also possible alternative flows. As shown in 9.5, start state is represented by a solid circle and the end state is represented by a solid circle inside a circle. The activities are represented by rectangles with rounded corners alongwith their descriptions. Activities are nothing but the set of operations. After execution of these set of activities, a transition takes place to another activity. Transitions are represented by an arrow. When multiple activities are performed simultaneously, the situation is represented by a symbol “fork”. The parallel activities are combined after the completion of such activities by a symbol “join”. The number of fork and join in an activity diagram are same. The branches are used to describe what activities are performed after evaluating a set of conditions. Branches may also be represented as diamonds with multiple labeled exit arrows. A guard condition is a boolean expression and is also written alongwith branches. An activity diagram consisting of seven activities is shown in 9.5.

In activity diagram given in 9.5, Activity 2 and Activity 3 are performed simultaneously and combined by a join symbol. After Activity 4, a decision is represented by a diamond symbol and if the guard condition is true Activity 5 is performed, otherwise Activity 6 is performed. The fork has one incoming transition (Activity 1 is split into sub activities) and two outgoing transitions. Similarly join has two incoming transitions and one outgoing transition. The symbols of an activity diagram are given in table 9.1.

S.No

Symbol

Notation

Remarks

1

Fork

To represent multiple parallel activities i.e. an activity is split into two or more activities.

2

Join

To represent the combination of two or more parallel activities after completion of respective activities.

3

Transition

To represent transfer of flow of control from one activity to another.

4

Activity

To represent set of operations known as an activity.

5

Start

To represent start state of an activity diagram.

6

End

To represent end state of an activity diagram.

7

Branch

To represent the transfer of flow on the basis of evaluation of boolean expression known as guard condition.

Table 9.1: Symbols of an activity diagram

An activity diagram represents the flow of activities through the class. We may read the diagram from top to bottom i.e. from start symbol to end symbol. It provides basis for the path testing where we may like to execute each independent path of the activity diagram at least once.

We consider the program given in 9.6 for determination of division of a student. We give marks in three subjects as input to calculate the division of a student. There are three methods in this program getdata, validate and calculate. The activity diagram for validate and calculate functions is given in 9.7 and 9.8.

#include<iostream.h>

#include<conio.h>

class student

{

int mark1;

int mark2;

int mark3;

public:

void getdata()

{

cout<<"Enter marks of 3 subjects (between 0-100)\n";

cout<<"Enter marks of first subject:";

cin>>mark1;

cout<<"Enter marks of second subject:";

cin>>mark2;

cout<<"Enter marks of third subject:";

cin>>mark3;

}

void validate()

{

if(mark1>100||mark1<0||mark2>100||mark2<0||mark3>100||mark3<0){

cout<<"Invalid Marks! Please try again";

}

else{

calculate();

}

}

void calculate();

};

void student::calculate()

{

int avg;

avg=(mark1+mark2+mark3)/3;

if(avg<40) {

cout<<"Fail";

}

else if(avg>=40&&avg<50){

cout<<"Third Division";

}

else if(avg>=50&&avg<60){

cout<<"Second Division";

}

else if(avg>=60&&avg<75){

cout<<"First Division";

}

else{

cout<<"First Division with Distinction";

}

}

void main()

{

clrscr();

student s1;

s1.getdata();

s1.validate();

getch();

}

9.3.2 Calculation of Cyclomatic Complexity

As defined earlier in chapter 4, cyclomatic complexity of a graph is given as:

V(G)=e-n+2P

Where e: number of edges of a graph G

n: number of nodes of a graph G

P: number of connected components

The same concepts of flow graph are applicable to an activity diagram, for the calculation of cyclomatic complexity. Nodes of a flow graph are represented as branches, activities, initial state and end state in an activity diagram. The edges of a flow graph are represented as transitions in the activity diagram. We may calculate the cyclomatic complexity in the same way and cyclomatic complexity of an activity diagram given in 9.5 is 2. Hence, there are two independent paths in the activity diagram.

We consider the activity diagram given in 9.7 for validate function and cyclomatic complexity is calculated as

Cyclomatic complexity = e-n+2P =transitions - activities/branches +2P

= 5 - 5 +2

= 2

Similarly, for activity diagram for calculate function given in 9.8 cyclomatic complexity is:

Cyclomatic complexity = e-n+2P

= 15 - 12 + 2

= 5

Hence, there are two and five independent paths of validate and calculate functions, respectively.

9.3.3 Generation of Test Cases

After the identification of independent paths, we may generate test cases that traverse all independent paths at the time of executing the program. This process will ensure that each transition of the activity diagram is traversed at least once.

In general, path testing may detect only errors that are resulting from executing a path in the program. It may not be able to detect the errors due to omissions of some important characteristics of the program. It is heavily dependent on the control structure of the program and if we execute all paths (if possible), an effective coverage is achieved. This effective coverage may contribute to the delivery of good quality maintainable software.

Test cases from activity diagrams of validate and calculate functions (refer s 9.7 and 9.8) are shown in table 9.2 and table 9.3.

Path testing is very useful in object oriented systems. Activity diagram provides a pictorial view of a class which helps us to identify various independent paths. However, as the size of a class increases design of an activity diagram becomes complex and difficult. This technique is applicable to the classes of reasonable size.

Test case

mark1

mark2

mark3

Path

1

101

40

50

Invalid marks

2

90

75

75

calculate()

Table 9.2: Test cases for validate function

Test case

mark1

mark2

mark3

Path

1

40

30

40

Fail

2

45

47

48

Third division

3

55

57

60

Second division

4

70

65

60

First division

5

80

85

78

First division with distinction

Table 9.3: Test cases for calculate function

Example 9.1: Consider the program given in 9.9 for determination of largest amongst three numbers. There are three methods in this program getdata, validate and maximum. Design test cases for validate and maximum methods of the class using path testing.

#include<iostream.h>

#include<conio.h>

class greatest

{

float A;

float B;

float C;

public:

void getdata()

{

cout<<"Enter number 1:\n";

cin>>A;

cout<<"Enter number 2:\n";

cin>>B;

cout<<"Enter number 3:\n";

cin>>C;

}

void validate()

{

if(A<0||A>400||B<0||B>400||C<0||C>400){

cout<<"Input out of range";

}

else{

maximum();

}

}

void maximum();

};

void greatest::maximum()

{

/*Check for greatest of three numbers*/

if(A>B) {

if(A>C) {

cout<<A;

}

else {

cout<<C;

}

}

else {

if(C>B) {

cout<<C;

}

else {

cout<<B;

}

}

}

void main()

{

clrscr();

greatest g1;

g1.getdata();

g1.validate();

getch();

}

The activity diagram for validate and calculate functions is given in 9.10 and 9.11 and their test cases are shown in table 9.4 and table 9.5.

Test case

A

B

C

Path

1

500

40

50

Input out of range

2

90

75

75

maximum()

Table 9.4: Test cases of activity diagram in 9.10

Cyclomatic complexity = e-n+2P =transitions - activities/branches +2P

= 5 - 5 +2

= 2

Cyclomatic complexity = e-n+2P =transitions - activities/branches +2P

11-9+2=4

Test case

A

B

C

Expected output

1

100

87

56

100

2

87

56

100

100

3

56

87

100

100

4

87

100

56

100

Table 9.5: Test cases of activity diagram given in 9.11

9.4 State Based Testing

State based testing is used as one of the useful object oriented software testing technique. It uses the concept of state machine of electronics circuits where output of the state machine is dependent not only on present state but also on the past state. A state represents the effect of previous inputs. Hence, in state machine, output is not only dependent on present inputs but also on previous inputs. In electronic circuits, such circuits are called sequential circuits. If output of a state is only dependent on present inputs, such circuits are called combinational circuits. In state based testing, resulting state is compared with the expected state.

9.4.1 What is a State Machine?

State machines are used to model the behavior of objects. A state machine represents various states which an object is expected to visit during its lifetime in response to events or methods alongwith its responses to these events or methods. A state is represented by rectangles with rounded corners and transitions are represented by edges (arrows). Events and actions are represented by annotations on the directed edges. A typical state machine is shown in 9.12 and descriptions of its associated terms are given in table 9.6.

S. No

Terminologies used in statechart diagram

Description

Remarks

1

State

Abstract situation in the life cycle of an entity that occurs in response to occurrence of some event.

State1, state2

2

Event

An input (a message or method call).

A,B

3

Action

An output or the result of an activity.

Next, previous

4

Transition

Change of state after occurrence of an event.

When x>y and A is the input, state is changed from state1 to state2

5

Guard condition

Predicate expression with an event, stating a boolean restriction for a transition to fire.

Two predicate expression x>y and x<y

Table 9.6: Terminologies used in statechart diagram

In the 9.12, there are two states state1 and state2. If at state1, input A is given and (x>y), then state1 is changed to state2 with an output ‘next'. At state2, if input is B and (x<y), then state2 is changed to state1 with an output ‘previous'. Hence, a transition transfers a system from one state to another state. First state is called accepting state and another is called resultant state. Both states (accepting and resultant) may also be same in case of self loop conditions. The state in question is the current state or present state. Transition occurs from current state to resultant state.

We consider an example of a process i.e. program under execution and may have the following states:

§ New: The process is created

§ Ready: The process is waiting for processor to be allocated.

§ Running: Process is allocated processor and is being executed.

§ Time expired: The time allocated to the process in execution expires.

§ Waiting: The process is waiting for some I/O or event to occur.

§ Terminated: Process under execution has completed.

The state machine for life cycle of a process is shown in 9.13. There are six states in the state machine namely new, ready, running, time expired, waiting and terminated. The waiting state is decomposed into three concurrent substates: I/O operation, child process and interrupt process. The three processes are separated by dashed lines. After the completion of these substates the flow of control joins to the ready state.

9.4.2 State Chart Diagram

In unified modeling language (UML), a state machine is graphically represented by a state chart diagram. It shows the flow of control from one state to another state. Here, also states are represented by rectangles with rounded corners and transitions are represented by edges (arrows).

Two special states are used i.e. α (alpha) and ω (omega) state for representing constructor and destructor of a class. These states may simplify testing of multiple constructors, exception handling and destructors. Binder [BIND99] has explained this concept very effectively as:

“The α state is a null state representing the declaration of an object before its construction. It may accept only a constructor, new, or a similar initialization message. The ω state is reached after an object has been destructed or deleted, or has gone out of scope. It allows for explicit modeling and systematic testing of destructors, garbage collection, and other termination actions”.

Alpha and omega states are different from start state and end state of a state chart diagram. These are additional states to make things more explicit and meaningful.

We consider an example of a class “stack” where two operations push and pop are allowed. The functionality of a stack suggests three states: empty, holding and full. There are four events namely new, push, pop and destroy with the following purposes:

* New: Creates an empty stack

* Push: Push an element in the stack, if space is available.

* Pop: Pop out an element from the stack, if it is available

* Destroy: destroy the stack after the completion of its requirement i.e. instance of the stack class is destroyed.

The state chart diagram for class stack is given in the 9.14.

9.4.3 State Transition Tables

State chart diagrams provide graphical view of the system and help us to understand the behaviour of the system. Test cases may be designed on the basis of understanding the behaviour. However, drawing a large state chart diagram is difficult, risky and error prone. If states are more than 10 or 15, it is difficult to keep track of various transitions. In practice, we may have to handle systems with 100 states or more state transition tables are used, when number of states are more and provide information in a compact tabular form. In state transition tables, rows represent present acceptable state and columns represent resultant state. The state transition table of class stack is given in table 9.7.

State transition tables represent every transition, event and action and may help us to design the test cases.

State

Event/method

Resultant state

Alpha

Empty

Holding

Full

Omega

Alpha

new


push(x)

pop()

destroy

Empty

new

push(x)


pop()

destroy

Holding

new

push(x)



pop()



destroy

Full

new

push(x)

pop()


destroy

Table 9.7: State transition table for stack class

9.4.4 Generation of test cases

There are many possible testing strategies for the generation of test cases. We may identify paths from the state chart diagram and execute all of them. This seems to be difficult in practice due to large number of paths. Another option is to exercise all transitions at least once. This may mean that all events coverage, all states coverage and all actions coverage. State chart diagram and state transition tables may help us to do so and we may be able to generate good number of systematic and planned test cases. The test cases for stack class are given in table 9.8. Here we generate test cases for each independent path in the state transition diagram given in 9.12. Some illegal transitions are also shown in table 9.9 in order to give an idea about undesired actions. What will happen if an illegal event is given to a state? These test cases are also important and may help to find fault in the state chart diagram.

Test case id

Test case input

Expected result

Event (method)

Test condition

Action

State

1.1

New

Empty

1.2

Push(x)

Holding

1.3

Pop()

Top=1

Return x

Empty

1.4

destroy

Omega

2.1

New

Empty

2.2

Push(x)

Holding

2.3

Pop()

Top>1

Return x

holding

2.4

destroy

Omega

3.1

New

Empty

3.2

Push(x)

Top<max-1

Holding

3.3

Push(x)

holding

3.4

destroy

Omega

4.1

New

Empty

4.2

Push(x)

Holding

4.3

Push(x)

Top=max-1

Full

4.4

Pop()

Holding

4.5

destroy

Omega

5.1

New

Empty

5.2

Push(x)

Holding

5.3

Push(x)

Top=max-1

Full

5.4

destroy

omega

6.1

New

empty

6.2

destroy

Omega

Table 9.8: Test cases for class stack

Test case id

Test condition

Expected result

Test state

Test event

Action

7.0

Empty

New

Illegal exception

8.0

Empty

Pop()

Illegal exception

9.0

Holding

New

Illegal exception

10.0

Holding

Push (top=max)

Illegal exception

11.0

Holding

Pop (top=0)

Illegal exception

12.0

Full

New

Illegal exception

13.0

Full

Push

Illegal exception

14.0

0mega

any

Illegal exception

Table 9.9: Illegal test case for class stack

Example 9.2: Consider the example of withdrawing cash from ATM machine. The process consists of the following steps:

(i) Customer will be asked to insert card and enter PIN number.

(ii) If the bank will validate the PIN number, then the withdrawal transaction will be performed:

a. Customer selects amount.

b. The system verifies that if it has sufficient money to satisfy the request, then the appropriate amount of cash is dispensed by the machine and a receipt is issued.

c. If sufficient amount is not available in the account, a message “Balance not sufficient” is issued.

(iii) If the bank reports that the customer's PIN is invalid, then the customer will have to re-enter the PIN. Draw Statechart diagram and generate test cases using state based testing.

Solution:

Statechart diagram for withdrawal of cash from ATM machine is shown in 9.15 and test cases are given in table 9.10.

Test case ID

Test case input

Expected output

Event

Test condition

Action

State

1.1

New

Insert card

1.2

Getting information

Enter pin

1.3

Validate

Validating PIN

1.4

Disapproved

Invalid pin

1.5

Transaction not completed

Collect card

Ejecting card

1.6

Destroy

Omega

2.1

New

Insert card

2.2

Getting information

Enter pin

2.3

Validate

Validating

2.4

Approved

Enter amount

2.5

Validate

Validating balance

2.6

Disapproved

Balance<amount

Balance not sufficient

2.7

Transaction not completed

Collect card

Ejecting card

2.8

Destroy

Omega

3.1

New

Insert card

3.2

Getting information

Enter pin

3.3

Validate

Validating

3.4

Approved

Enter amount

3.5

Validate

Validating balance

3.6

Debit amount

Balance>=amount

Balance=balance-amount

3.7

Collect cash

Money dispensed from slot

3.8

Print receipt

Collect receipt

Printing

3.9

Transaction completed

Collect card

Ejecting card

3.10

Destroy

Omega

Table 9.10: Test cases of Withdrawal from ATM

9.5 Class Testing

A class is very important in object oriented programming. Every instance of a class is known as an object. Testing of a class is very significant and critical in object oriented testing where we want to verify the implementation of a class with respect to its specifications. If the implementation is as per specifications, then it is expected that every instance of the class may behave in the specified way. Class testing is similar to the unit testing of conventional system. We require stubs and drivers for testing a “unit” and sometimes, it may require significant effort. Similarly, classes also cannot be tested in isolation. They may also require additional source code (similar to stubs and drivers) for testing independently.

9.5.1 How should we test a class?

We want to test the source code of a class. Validation and verification techniques are equally applicable to test a class. We may review the source code during verification and may be able to detect good number of errors. Reviews are very common in practice, but their effectiveness is heavily dependent on the ability of reviewer(s).

Another type of testing is validation where we may execute a class using a set of test cases. This is also common in practice but significant effort may be required to write test drivers and sometime this effort may be more than the effort of developing the “unit” under test. After writing test cases for a class, we must design a test driver to execute each of the test cases and record the output of every test case. The test driver creates one or more instances of a class to execute a test case. We should always remember that classes are tested by creating instances and testing the behavior of those instances [MCGR01].

9.5.2 Issues related to class testing

How should we test a class? We may test it independently, as a unit or as group of a system. The decision is dependent on the amount of effort required to develop a test driver, severity of class in the system and associated risk with it and so on. If a class has been developed to be a part of class library, thorough testing is essential even if the cost of developing a test driver is very high.

Classes should be tested by its developers after developing a test driver. Developers are familiar with the internal design, complexities and other critical issues of a class under test and this knowledge may help to design test cases and develop test driver (s). Class should be tested with respect to its specifications. If some unspecified behaviours have been implemented, we may not be able to test them. We should always be very careful for additional functionalities which are not specified. Generally, we should discourage this practice and if it has been implemented in the SRS document, it should immediately be specified. A test plan with a test suite may discipline the testers to follow a predefined path. This is particularly essential when developers are also the testers.

9.5.3 Generating test cases

One of the methods of generating test cases is from pre and post conditions specified in the use cases. As discussed in chapter 6, use cases help us to generate very effective test cases. The pre and post conditions of every method may be used to generate test cases for class testing. Every method of a class has a precondition that need to be satisfied before the execution. Similarly, every method of a class has a post condition that is the resultant state after the execution of the method. Consider a class “stack” given in 9.16 with two attributes (x and top) and three methods (stack(), push(x), pop()).

We should first specify the pre and post conditions for every operation/method of a class. We may identify requirements for all possible combinations of situations in which a precondition can hold and post conditions can be achieved. We may generate test cases to address what happens when a precondition is violated [MCGR01]. We consider the stack class given in 9.16 and identify the following pre and post conditions of all the methods in the class.

(i) Stack::stack()

a. Pre=true

b. Post: top=0

(ii) Stack::push(x)

a. Pre: top<MAX

b. Post: top=top+1

(iii) Stack::pop()

a. Pre: top>0

b. Post: top=top-1

After the identification of pre and post conditions, we may establish logical relationships between pre and post conditions. Every logical relationship may generate a test case. We consider the push() operation and establish the following logical relationships:

1. (pre condition: top<MAX; post condition: top=top+1)

2. (pre condition: not (top<MAX) ; post condition: exception)

Similarly for pop() operation, following logical relationships are established:

3. (pre condition: top>0; post condition: top=top-1)

4. (pre condition: not (top>0) ; post condition: exception)

We may identify test cases for every operation/method using pre and post conditions. We should generate test cases when a precondition is true and false. Both are equally important to verify the behaviour of a class. We may generate test cases for push(x) and pop() operations (refer table 9.11 and table 9.12).

Test input

Condition

Expected output

23

Top<MAX

Element '23' inserted successfully

34

Top=MAX

Stack overflow

Table 9.11: Test cases of function push()

Test input

Condition

Expected output

-

Top>0

23

-

Top=0

Stack underflow

Table 9.12: Test cases of function pop()

Example 9.3: Consider the example of withdrawing cash from ATM machine given in example 9.2. Generate test cases using class testing.

Solution:

The class ATMWithdrawal is given in 9.17.

ATMWithdrawal

accountID: integer

Amount: integer

ATMWithdrawal (accid, amt)

Withdraw()

The pre and post conditions of function Withdraw() are given as:

ATMWirthdrawal::Withdraw()

Pre: true

Post: if(PIN is valid) then

If (balance>=amount) then

Balance=balance-amount

Else

Display “Insufficient balance”

Else

Display “Invalid PIN”

(true, PIN is valid and balance>=amount)

(true, PIN is valid and balance<amount)

(true, PIN is invalid)

Test cases are given in table 9.13.

S.No

AccountID

Amount

Expected output

1

4321

1000

Balance update/Debit account

2

4321

2000

Insufficient balance

3

4322

-

Invalid PIN

Table 9.13: Test cases for function withdraw()

MULTIPLE CHOICE QUESTIONS

Note: Select most appropriate answer of the following questions.

9.1 A class has:

(a) Attributes and operations

(b) Attributes and states

(c) Operations and Behaviour

(d) State and information

9.2 An object is:

(a) An information of a class

(b) An instance of a class

(c) An attribute of a class

(d) An operation of a class

9.3 The objects of the same class have

(a) Different definition for operations and information

(b) Same definition for operations and information

(c) Different operations

(d) Different formats

9.4 All classes inherit information from

(a) The lower classes

(b) The same classes

(c) The upper classes

(d) The lower and upper classes

9.5 Encapsulation is known as:

(a) Information sharing concept

(b) Information retrieval concept

(c) Information hiding concept

(d) Information transfer concept

9.6 A method is

(a) The sequence of steps to be performed to fulfill the assigned task

(b) The set of operations for a particular task

(c) Both (a) and (b)

(d) None of the above

9.7 Which is not a defined testing level?

(a) Method testing

(b) Class testing

(c) Interclass testing

(d) Regression testing

9.8 Which is not a symbol of an activity diagram?

(a) Join

(b) Fork

(c) Operation

(d) Transition

9.9 An activity diagram represent the flow of activities through the:

(a) Classes

(b) Methods

(c) Objects

(d) Programs

9.10 Path testing may not be able to detect errors due to

(a) Omissions of some characteristics of program

(b) Implementation

(c) Complexity of the code

(d) None of the above

9.11 In state chart diagrams, transitions are represented by

(a) Edges

(b) States

(c) Variables

(d) Circles

9.12 In state chart diagrams, states are represented by:

(a) Edges

(b) Rounded rectangles

(c) Circles

(d) Arrows

9.13 Guard condition in state chart diagram is :

(a) Predicate expression with an event

(b) Regular expression

(c) Sequential expression

(d) Last expression

9.14 What is ά state?

(a) Intermediate state

(b) Null state

(c) End state

(d) Initial state

9.15 What is ω state?

(a) Last state

(b) Intermediate state

(c) Start state

(d) Initial state

9.16 Which is not an object oriented testing technique?

(a) State based testing

(b) Class testing

(c) Equivalence class testing

(d) Path testing

9.17 Classes are tested by creating

(a) Another classes

(b) Polymorphism

(c) Class hierarchy

(d) Instances

9.18 What types of things become objects?

(a) Real World entities

(b) Humans

(c) Humans and animals

(d) Any living thing

9.19 Classes should be tested by its

(a) Customers

(b) Developers

(c) Testers

(d) Managers

9.20 Object orientation is centered around concepts like

(a) Objects and classes

(b) Messages and inheritance

(c) Polymorphism and encapsulation

(d) All of the above

Writing Services

Essay Writing
Service

Find out how the very best essay writing service can help you accomplish more and achieve higher marks today.

Assignment Writing Service

From complicated assignments to tricky tasks, our experts can tackle virtually any question thrown at them.

Dissertation Writing Service

A dissertation (also known as a thesis or research project) is probably the most important piece of work for any student! From full dissertations to individual chapters, we’re on hand to support you.

Coursework Writing Service

Our expert qualified writers can help you get your coursework right first time, every time.

Dissertation Proposal Service

The first step to completing a dissertation is to create a proposal that talks about what you wish to do. Our experts can design suitable methodologies - perfect to help you get started with a dissertation.

Report Writing
Service

Reports for any audience. Perfectly structured, professionally written, and tailored to suit your exact requirements.

Essay Skeleton Answer Service

If you’re just looking for some help to get started on an essay, our outline service provides you with a perfect essay plan.

Marking & Proofreading Service

Not sure if your work is hitting the mark? Struggling to get feedback from your lecturer? Our premium marking service was created just for you - get the feedback you deserve now.

Exam Revision
Service

Exams can be one of the most stressful experiences you’ll ever have! Revision is key, and we’re here to help. With custom created revision notes and exam answers, you’ll never feel underprepared again.