ST Lab File PDF
ST Lab File PDF
Aim
Write a program to find the maximum in three numbers input by the user and generate test
cases for the program using Boundary Value Analysis.
Related Theory
It is a type of functional testing. It concentrates on minimum value, just above minimum,
maximum, just blow maximum and nominal value. Total no of Test cases= 4*n+1 where
n:total no of inputs
Algorithm
1. Identify the testing technique that is to be performed.
2. Identify the input variables.
3. Check whether that technique obeys Single Fault Assumption.
4. Write down the test cases.
5. Test the program.
Program
#include<iostream>
#include<vector>
#include<cstring>
// Function to be tested
int find_min(int nums[] , int len){
int max = -99999999;
return max;
}
memcpy(this->variables,variables,sizeof(int)*num_vars);
}
void run(){
this->eo = find_min(variables,num_vars);
}
void print_result(){
// Print the testcase
cout<<"Variables : ";
for (int i = 0; i < num_vars; ++i)
{
cout<<variables[i]<<" ";
}
Testsuite(){
num_cases = 0;
}
num_cases += v.size();
for (std::vector<Testcase>::iterator i = v.begin(); i != v.end(); ++i)
{
this->cases.push_back(*i);
}
void run(){
for (std::vector<Testcase>::iterator i = cases.begin(); i != cases.end(); ++i)
{
(*i).run();
}
}
void print_results(){
}
};
return Testcase(num_vars,variables);
}
// 1. Minimum
variables[i] = ranges[i].first;
v.push_back(Testcase(num_vars,variables));
// 2. Minimum + 1
variables[i] = ranges[i].first + 1;
v.push_back(Testcase(num_vars,variables));
// 3. Maximum - 1
variables[i] = ranges[i].second-1;
v.push_back(Testcase(num_vars,variables));
// 4. Maximum
variables[i] = ranges[i].second;
v.push_back(Testcase(num_vars,variables));
suite.add_all(v);
}
return suite;
}
// Driver function
int main(int argc, char const *argv[])
{
int num_vars = 0;
cout<<"Enter number of variables : ";
cin>>num_vars;
pair<int,int> ranges[num_vars];
for (int i = 0; i < num_vars; ++i)
{
cout<<"Enter minimum and maximum value of variable "<<i+1<<" : ";
cin>>ranges[i].first>>ranges[i].second;
}
Theory
It is a type of functional testing. It concentrates on minimum value, just above minimum,
maximum, just below maximum, nominal, just below minimum and just above maximum
value. Total no of Test cases= 6*n+1 where n:total no of inputs
Algorithm
1. Identify the testing technique that is to be performed .
2. Identify the input variables.
3. Check whther that technique obeys Single Fault Assumption.
4. Write down the test cases.
5. Test the program.
Program
#include<iostream>
#include<vector>
#include<cstring>
// Function to be tested
int find_min(int nums[] , int len){
int max = -99999999;
return max;
}
memcpy(this->variables,variables,sizeof(int)*num_vars);
}
void run(){
this->eo = find_min(variables,num_vars);
}
void print_result(){
// Print the testcase
cout<<"Variables : ";
for (int i = 0; i < num_vars; ++i)
{
cout<<variables[i]<<" ";
}
Testsuite(){
num_cases = 0;
}
num_cases += v.size();
for (std::vector<Testcase>::iterator i = v.begin(); i != v.end(); ++i)
{
this->cases.push_back(*i);
}
void run(){
for (std::vector<Testcase>::iterator i = cases.begin(); i != cases.end(); ++i)
{
(*i).run();
}
}
void print_results(){
}
};
Testcase get_nominal_testcase(int num_vars, pair<int,int> ranges[]){
int variables[num_vars];
for (int i = 0; i < num_vars; ++i)
{
variables[i] = (ranges[i].second - ranges[i].first)/2;
}
return Testcase(num_vars,variables);
}
// 1. Minimum - 1
variables[i] = ranges[i].first-1;
v.push_back(Testcase(num_vars,variables));
// 2. Minimum
variables[i] = ranges[i].first;
v.push_back(Testcase(num_vars,variables));
// 2. Minimum + 1
variables[i] = ranges[i].first + 1;
v.push_back(Testcase(num_vars,variables));
// 3. Maximum - 1
variables[i] = ranges[i].second-1;
v.push_back(Testcase(num_vars,variables));
// 4. Maximum
variables[i] = ranges[i].second;
v.push_back(Testcase(num_vars,variables));
// 5. Maximum + 1
variables[i] = ranges[i].second+1;
v.push_back(Testcase(num_vars,variables));
suite.add_all(v);
}
// Driver function
int main(int argc, char const *argv[])
{
int num_vars = 0;
cout<<"Enter number of variables : ";
cin>>num_vars;
pair<int,int> ranges[num_vars];
for (int i = 0; i < num_vars; ++i)
{
cout<<"Enter minimum and maximum value of variable "<<i+1<<" : ";
cin>>ranges[i].first>>ranges[i].second;
}
Output
Theory
It is a type of functional testing. It concentrates on minimum value, just above minimum,
maximum, just below maximum and nominal value for each input variable. It does not obey
Single Fault Assumption. Total no of Test cases= 5n where n:total no of input
Algorithm
1. Identify the testing technique that is to be performed .
2. Identify the input variables.
3. Check whther that technique obeys Single Fault Assumption.
4. Write down the test cases.
5. Test the program.
Program
#include<iostream>
#include<vector>
#include<cstring>
// Function to be tested
int find_min(int nums[] , int len){
int max = -99999999;
return max;
}
memcpy(this->variables,variables,sizeof(int)*num_vars);
}
void run(){
this->eo = find_min(variables,num_vars);
}
void print_result(){
// Print the testcase
cout<<"Variables : ";
for (int i = 0; i < num_vars; ++i)
{
cout<<variables[i]<<" ";
}
Testsuite(){
num_cases = 0;
}
num_cases += v.size();
for (std::vector<Testcase>::iterator i = v.begin(); i != v.end(); ++i)
{
this->cases.push_back(*i);
}
void run(){
for (std::vector<Testcase>::iterator i = cases.begin(); i != cases.end(); ++i)
{
(*i).run();
}
}
void print_results(){
}
};
// 2. Minimum
variables[i] = ranges[i].first;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 3. Minimum + 1
variables[i] = ranges[i].first + 1;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 4. Nominal
variables[i] = (ranges[i].second - ranges[i].first)/2;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 5. Maximum - 1
variables[i] = ranges[i].second-1;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 6. Maximum
variables[i] = ranges[i].second;
generate_testcases(num_vars,ranges,variables,i+1,suite);
return suite;
}
// Driver function
int main(int argc, char const *argv[])
{
int num_vars = 0;
cout<<"Enter number of variables : ";
cin>>num_vars;
pair<int,int> ranges[num_vars];
for (int i = 0; i < num_vars; ++i)
{
cout<<"Enter minimum and maximum value of variable "<<i+1<<" : ";
cin>>ranges[i].first>>ranges[i].second;
}
Theory
It is a type of functional testing. It concentrates on minimum value, just above minimum,
maximum, just below maximum, nominal, just below minimum and just above maximum
value for each input variable. It does not obey Single Fault Assumption. Total no of Test
cases= 7n where n:total no of input
Algorithm
1. Identify the testing technique that is to be performed .
2. Identify the input variables.
3. Check whther that technique obeys Single Fault Assumption.
4. Write down the test cases.
5. Test the program.
Program
#include<iostream>
#include<vector>
#include<cstring>
// Function to be tested
int find_min(int nums[] , int len){
int max = -99999999;
return max;
}
memcpy(this->variables,variables,sizeof(int)*num_vars);
}
void run(){
this->eo = find_min(variables,num_vars);
}
void print_result(){
// Print the testcase
cout<<"Variables : ";
for (int i = 0; i < num_vars; ++i)
{
cout<<variables[i]<<" ";
}
Testsuite(){
num_cases = 0;
}
num_cases += v.size();
for (std::vector<Testcase>::iterator i = v.begin(); i != v.end(); ++i)
{
this->cases.push_back(*i);
}
void run(){
for (std::vector<Testcase>::iterator i = cases.begin(); i != cases.end(); ++i)
{
(*i).run();
}
}
void print_results(){
}
};
// 1. Minimum - 1
variables[i] = ranges[i].first-1;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 2. Minimum
variables[i] = ranges[i].first;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 3. Minimum + 1
variables[i] = ranges[i].first + 1;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 4. Nominal
variables[i] = (ranges[i].second - ranges[i].first)/2;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 5. Maximum - 1
variables[i] = ranges[i].second-1;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 6. Maximum
variables[i] = ranges[i].second;
generate_testcases(num_vars,ranges,variables,i+1,suite);
// 7. Maximum + 1
variables[i] = ranges[i].second+1;
generate_testcases(num_vars,ranges,variables,i+1,suite);
return suite;
}
// Driver function
int main(int argc, char const *argv[])
{
int num_vars = 0;
cout<<"Enter number of variables : ";
cin>>num_vars;
pair<int,int> ranges[num_vars];
for (int i = 0; i < num_vars; ++i)
{
cout<<"Enter minimum and maximum value of variable "<<i+1<<" : ";
cin>>ranges[i].first>>ranges[i].second;
}
Theory
It is a type of functional testing. The input domain is divided into various categories with
some relationship and it is expected that every test case from a category exhibits the same
behaviour. Each category is called an Equivalence class.
Algorithm
1. Identify the input domain and output domain.
2. Write the equivalence classes for both input and output domain.
3. Test the program using these equivalence classes.
Program
#include<iostream>
#include<conio.h>
generateClasses();
cout<<"\nInput Classes...";
for(i=0;i<20;i++)
cout<<"\nI"<<i<<": "<<inputClass[i];
cout<<"\n\nOutput Classes...";
for(i=0;i<4;i++)
cout<<"\nO"<<i<<": "<<outputClass[i];
cout<<"\n\nTest Cases...";
cout<<"\n+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+";
cout<<"\n|\t\t Input\t\t\t| Expected\t\t|";
cout<<"\n|\ta\t\tb\t\tc\t| Output\t\t|";
cout<<"\n+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+\n";
for(int i=0;i<24;i++)
{
for(int j=0;j<3;j++)
{
if(j==0) cout<<"|";
cout<<"\t"<<testCases[i][j]<<"\t|";
}
int expOut=triangle(testCases[i],min,max);
if(expOut<0)
cout<<" Invalid input\t";
else cout<<" "<<outputClass[expOut];
cout<<"\t|"<<endl;
}
cout<<"+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+";
cout<<"\n>>No. of Test Cases = "<<sizeof(inputClass)/sizeof(inputClass[0]) +
sizeof(outputClass)/sizeof(outputClass[0]);
}
int main()
{
int min[3], max[3];
for(int i=0;i<3;i++)
{
cout<<"Enter min & max value of vertex "<<i+1<<" : ";
cin>>min[i]>>max[i];
}
equivalenceTesting(min,max);
return 0;
}
Output
Enter min & max value of vertex 1 : 1 100
Enter min & max value of vertex 2 : 2 80
Enter min & max value of vertex 3 : 1 50
Input Classes...
I0: {x : x<xmin}
I1: {x : x>xmax}
I2: {x : xmin<x<xmax}
I3: {y : y<ymin}
I4: {y : y>ymax}
I5: {y : ymin<y<ymax}
I6: {z : z<zmin}
I7: {z : z>zmax}
I8: {z : zmin<z<zmax}
I9: {x,y,z : x=y=z}
I10: {x,y,z : x=y,x!=z}
I11: {x,y,z : x=z,x!=y}
I12: {x,y,z : y=z,x!=y}
I13: {x,y,z : x!=y!=z}
I14: {x,y,z : x=y+z}
I15: {x,y,z : x>y+z}
I16: {x,y,z : y=x+z}
I17: {x,y,z : y>x+z}
I18: {x,y,z : z=x+y}
I19: {x,y,z : z>x+y}
Output Classes...
O0: Equilateral Triangle
O1: Isoscles Triangle
O2: Scalene Triangle
O3: Not a Triangle
Test Cases...
+-----------------------------------------------------------------------+
| Input | Expected |
| a b c | Output |
+-----------------------------------------------------------------------+
| 0 | 41 | 25 | Invalid input |
| 101 | 41 | 25 | Invalid input |
| 50 | 41 | 25 | Scalene Triangle |
| 50 | 1 | 25 | Invalid input |
| 50 | 81 | 25 | Invalid input |
| 50 | 41 | 25 | Scalene Triangle |
| 50 | 41 | 0 | Invalid input |
| 50 | 41 | 101 | Invalid input |
| 50 | 41 | 25 | Scalene Triangle |
| 60 | 60 | 60 | Invalid input |
| 60 | 60 | 25 | Isoscles Triangle |
| 60 | 41 | 60 | Invalid input |
| 60 | 60 | 50 | Isoscles Triangle |
| 40 | 60 | 80 | Invalid input |
| 66 | 41 | 25 | Not a Triangle |
| 67 | 41 | 25 | Not a Triangle |
| 50 | 75 | 25 | Not a Triangle |
| 50 | 76 | 25 | Not a Triangle |
| 50 | 41 | 91 | Invalid input |
| 50 | 41 | 92 | Invalid input |
| 60 | 60 | 60 | Invalid input |
| 60 | 60 | 70 | Invalid input |
| 50 | 60 | 70 | Invalid input |
| 20 | 60 | 100 | Invalid input |
+-----------------------------------------------------------------------+
>>No. of Test Cases = 24
Related Theory :
The techniques of equivalence partitioning and boundary value analysis are often applied to
specific situations or inputs. However, if different combinations of inputs result in different
actions being taken, this can be more difficult to show using equivalence partitioning and
boundary value analysis, which tend to be more focused on the user interface. Decision tables
are used in many engineering disciplines to represent complex logical relationships. An
output may be dependent on many input conditions and decision tables give a pictorial view
of various combinations of input conditions
Condition Stubs: All the conditions are represented in this upper left section of the decision
table. These conditions are used to determine a particular action or set of actions.
Action Stubs: All possible actions are listed in this lower left portion of the decision table.
Condition Entries: In the condition entries portion of the decision table, we have a number of
columns and each column represents a rule. Values entered in this upper right portion of the
table are known as inputs
Action Entries: Each entry in the action entries portion has some associated action or set of
actions in this lower right portion of the table. These values are known as outputs and are
dependent upon the functionality of the program.
Algorithm:
1) The first task is to identify a suitable function or subsystem which reacts according to
a combination of inputs or events. That is we have to identify the conditions and
actions.
2) The next step is to find the action corresponding to the various possible causes and
mark it to show that this action will be performed when the corresponding conditions
are satisfied.
Source Code :
#include<iostream>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
string conditionStub[6];
string actionStub[5];
char conditionEntries[6][11];
char actionEntries[6][11];
void generateStubAndEntries()
{
conditionStub[0] = "a<b+c?";
conditionStub[1] = "b<c+a?";
conditionStub[2] = "c<a+b?";
conditionStub[3] = "a=b?";
conditionStub[4] = "b=c?";
conditionStub[5] = "c=a?";
//for b<c+a?
i=0; conditionEntries[1][i]='-';
i=1; conditionEntries[1][i]='F';
for(i=2;i<11;i++)
conditionEntries[1][i]='T';
//for c<a+b?
i=0; conditionEntries[2][i]='-';
i=1; conditionEntries[2][i]='-';
i=2; conditionEntries[2][i]='F';
for(i=3;i<11;i++)
conditionEntries[2][i]='T';
//for a=b?
for(i=0;i<3;i++)
conditionEntries[3][i]='-';
for(i=3;i<11;i++)
if(i<7)
conditionEntries[3][i]='T';
else
conditionEntries[3][i]='F';
//for a=c?
for(i=0;i<3;i++)
conditionEntries[4][i]='-';
for(i=3;i<11;i++)
if(i<5 || (i>7&&i<9))
conditionEntries[4][i]='T';
else
conditionEntries[4][i]='F';
//for b=c?
for(i=0;i<3;i++)
conditionEntries[5][i]='-';
for(i=3;i<11;i++)
if(i%2)
conditionEntries[5][i]='T';
else
conditionEntries[5][i]='F';
actionEntries[0][0]='x';
actionEntries[0][1]='x';
actionEntries[0][2]='x';
actionEntries[3][3]='x';
actionEntries[4][4]='x';
actionEntries[4][5]='x';
actionEntries[2][6]='x';
actionEntries[4][7]='x';
actionEntries[2][8]='x';
actionEntries[2][9]='x';
actionEntries[1][10]='x';
cout<<"\nDecision Table...";
cout<<"+"; for(i=0;i<67;i++) cout<<"-"; cout<<"+\n";
for(i=0;i<6;i++)
{
cout<<"| "<<conditionStub[i]<<"\t\t";
if(i>=3) cout<<"\t";
cout<<"| ";
for(int j=0;j<11;j++)
{
cout<<conditionEntries[i][j]<<" | ";
}
cout<<endl;
}
cout<<"+"; for(i=0;i<67;i++) cout<<"-"; cout<<"+\n";
for(i=0;i<5;i++)
{
cout<<"| "<<actionStub[i]<<"\t";
if(i==4) cout<<"\t";
cout<<"| ";
for(int j=0;j<11;j++)
{
cout<<actionEntries[i][j]<<" | ";
}
cout<<endl;
}
cout<<"+"; for(i=0;i<67;i++) cout<<"-"; cout<<"+\n";
}
generateStubAndEntries();
for(i=0;i<11;i++)
{
switch(i)
{
case 0: x=100; y=60; z=20;
break;
case 1: x=60; y=100; z=20;
break;
case 2: x=20; y=60; z=100;
break;
case 3: x=y=z=60;
break;
case 4: x=y=z=-1;
break;
case 5: x=y=z=-1;
break;
case 6: x=y=60; z=70;
break;
case 7: x=y=z=-1;
break;
case 8: x=z=60; y=70;
break;
case 9: y=z=60; x=70;
break;
case 10: x=50; y=60; z=70;
break;
}
testCases[i][0]=x; testCases[i][1]=y; testCases[i][2]=z;
}
cout<<"\nTest Cases...";
cout<<"\n+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+";
cout<<"\n|\t\t Input\t\t\t| Expected\t\t|";
cout<<"\n|\ta\t\tb\t\tc\t| Output\t\t|";
cout<<"\n+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+\n";
for(int i=0;i<11;i++)
{
for(int j=0;j<3;j++)
{
if(j==0) cout<<"|";
cout<<"\t"<<testCases[i][j]<<"\t|";
}
int expOut=triangle(testCases[i],min,max);
if(expOut<0)
cout<<" Invalid input\t";
else cout<<" "<<actionStub[expOut];
cout<<"\t|"<<endl;
}
cout<<"+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+";
cout<<"\n>>No. of Test Cases = 11";
}
int main()
{
int min[3], max[3];
for(int i=0;i<3;i++)
{
cout<<"Enter min & max value of vertex "<<i+1<<" : ";
cin>>min[i]>>max[i];
}
decisionTableTesting(min,max);
return 0;
}
Output :
Decision Table...
+-------------------------------------------------------------------+
| a<b+c? |F|T|T|T|T|T|T|T|T|T|T|
| b<c+a? |-|F|T|T|T|T|T|T|T|T|T|
| c<a+b? |-|-|F|T|T|T|T|T|T|T|T|
| a=b? |-|-|-|T|T|T|T|F|F|F|F|
| b=c? |-|-|-|T|T|F|F|F|T|F|F|
| c=a? |-|-|-|T|F|T|F|T|F|T|F|
+-------------------------------------------------------------------+
| Not a triangle |x|x|x| | | | | | | | |
| Equilateral triangle | | | | | | | | | | | x |
| Isoscles triangle | | | | | | | x | | x | x | |
| Scalene triangle | | | |x| | | | | | | |
| Impossible | | | | |x|x| |x| | | |
+-------------------------------------------------------------------+
Test Cases...
+-----------------------------------------------------------------------+
| Input | Expected |
| a b c | Output |
+-----------------------------------------------------------------------+
| 100 | 60 | 20 | Not a triangle |
| 60 | 100 | 20 | Not a triangle |
| 20 | 60 | 100 | Not a triangle |
| 60 | 60 | 60 | Equilateral triangle |
| -1 | -1 | -1 | Invalid input |
| -1 | -1 | -1 | Invalid input |
| 60 | 60 | 70 | Isoscles triangle |
| -1 | -1 | -1 | Invalid input |
| 60 | 70 | 60 | Isoscles triangle |
| 70 | 60 | 60 | Isoscles triangle |
| 50 | 60 | 70 | Scalene triangle |
+-----------------------------------------------------------------------+
>>No. of Test Cases = 11
Discussion :
We successfully generated the test cases for the given program using Decision table based
testing. Decision tables are popular in circumstances where an output is dependent on many
conditions and a large number of decision are required to be taken. This technique is applied
easily at unit level only.
In this experiment we learned how to generate test cases by using Decision table based
testing technique.
Experiment No: 7
Aim : Write a program to find Cyclomatic complexity of a program.
Related Theory :
Algorithm:
McCabe introduced this concept and gave three methods to calculate cyclomatic complexity.
(i) V(G) = e – n + 2P
G : program graph
n : number of nodes
e : number of edges
The program graph (G) is a directed graph with single entry node and single exit node. A
connected graph is a program graph where all nodes are reachable from entry node, and exit
node is also reachable from all nodes. Such a program graph will have connected component
(P) value equal to one. If there are parts of the program graph, the value will be the number of
parts of the program graph where one part may represent the main program and other parts
may represent sub-programs.
(ii) Cyclomatic complexity is equal to the number of regions of the program graph.
V(G) = π + 1
Where π is the number of predicate nodes contained in the program graph (G).
Source Code :
#include<fstream>
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
main()
{
fstream testFile;
testFile.open("test.txt",ios::in);
int i=0, cyclo=0, len=0;
char a[1000];
if(testFile.is_open())
{
while(testFile)
{
a[i]=testFile.get();
cout<<a[i++];
}
len=strlen(a);
}
for(i=0;i<len-3;i++)
{
if(a[i]=='i')
{
i++;
if(a[i]=='f')
{
i++;
if(a[i]=='(')
cyclo++;
}
}
else if(a[i]=='w')
{
i++;
if(a[i]=='h')
{
i++;
if(a[i]=='i')
{
i++;
if(a[i]=='l')
{
i++;
if(a[i]=='e')
{
i++;
if(a[i]=='(')
cyclo++;
}
}
}}
}
}
cout<<"\n\nCyclomatic complexity: "<<cyclo+1;
testFile.close();
return 0;
}
Output :
Discussion :
High Testability
Medium Testability
Low Testability
Related Theory:
A graph matrix is a square matrix with one row and one column for every node of the graph.
The size of the matrix (number of rows and number of columns) is the number of nodes of
the
graph. Graph matrix is the tabular representation of a program graph.
If we assign weight for every entry in the table, then this may be used for the identification of
independent paths. The simplest weight is 1, if there is a connection and 0 if there is no
connection. Each row with more than one entry represents a predicate node and cyclomatic
complexity is predicate nodes plus one ( +1).
Source Code:
#include<iostream>
#define MAX 10
int main()
{
int row, col, i, j, edges=0, nodes=0;
char ch;
int matrix[MAX][MAX];
nodes = row;
cout<<"\nNumber of nodes = "<<nodes;
for(i=0;i<=row;i++)
for(j=0;j<=col;j++)
if(matrix[i][j]==1)
edges++;
Number of nodes = 4
Number of edges = 9
Discussion :
Graph matrices is a mathematical concept which is used in software testing to trace all possible paths in a graph,
at least once, and calculates the cyclomatic complexity. It converts a flow graph into matrix and each node
corresponds to particular row & column, by applying step by step process to get desired paths with its calculated
values. It represents a crucial relationship between nodes. It represents complex graphs in matrix form, and
applies multiple operations in matrices to trace a path. Each node in a graph holds particular value. Sometimes,
path can be traced twice which occurs redundancy or can be missed. These misconception and tracing errors
generate interruptions and leads us to confusion.
As we know, each graph matrix expresses a direct link between nodes. If we take the square
of the matrix, it shows 2-links relationships via one intermediate node. Hence, square matrix
represents all paths of two links long. The Kth power of matrix represents all paths of K links
long.
Program No: 9
Aim: Perform an experiment on Mutation Testing.
Related Theory:
It is a popular technique to assess the effectiveness of a test suite. We may have a large
number of test cases for any program. We neither have time nor resources to execute all of
them. We may select a few test cases using any testing technique and prepare a test suite.
Hence, assessing the effectiveness and quality of a test suite is very important. Mutation
testing may help us to assess the effectiveness of a test suite and may also enhance the test
suite, if it is not adequate for a program.
When we execute a mutant using a test suite, we may have any of the following outcomes:
(i) The results of the program are affected by the change and any test case of the test suite
detects it. If this happens, then the mutant is called a killed mutant.
(ii) The results of the program are not affected by the change and any test case of the test
suite does not detect the mutation. The mutant is called a live mutant.
The mutation score associated with a test suite and its mutants is calculated as:
The total number of mutants is equal to the number of killed mutants plus the number of
live mutants. The mutation score measures how sensitive the program is to the changes and
how accurate the test suite is. A mutation score is always between 0 and 1. A higher value of
mutation score indicates the effectiveness of the test suite although effectiveness also depends
on the types of faults that the mutation operators are designed to represent.
Program:
-->Mutations performed:
9) Change the access modifier from private to public
10) Change static modifier
11) Change argument modifier
12) Change the operator
13) Change the numeric value of operands
14) Calculate the mutation score
#include<iostream>
#include<conio.h>
#include<math.h>
using namespace std;
class Rectangle
{
int length;
int breadth;
static int count;
public:
Rectangle();
int getLength();
int getBreadth();
void storeData(int , int );
int calculateArea(int , int );
int objectCount();
};
Rectangle :: Rectangle()
{ count++;
length = 0;
breadth = 0;
}
int Rectangle :: getLength()
{ return length;
}
main()
{
char ch='y';
do{
Rectangle R;
int l, b;
cout<<"\nEnter the dimensions of the rectangle...\n";
cout<<"Length = ";
cin>>l;
cout<<"Breadth = ";
cin>>b;
R.storeData(l,b);
return 0;
}
Mutated Statements:
Mutant Line
Original Line Modified Line
No. No.
M1 13 public: private:
M2 11 static int count; int count;
41 void Rectangle :: storeData(int l, void Rectangle :: storeData(int b,
M3
int b) int l)
M4 49 return length * breadth; return length + breadth;
M5 27 length = 0; length = 10;
Mutant M1:
Test Cases length breadth Expected Output Actual Output
1 2 2 4 Compilation Error
2 3 5 15 Compilation Error
Output:
Mutant M2:
Test Cases length breadth Expected Output Actual Output
1 2 2 4 Compilation Error
2 3 5 15 Compilation Error
Output:
Mutant M3:
Test Cases length breadth Expected Output Actual Output
1 2 2 4 4
2 3 5 15 15
Output:
Mutant M4:
Test Cases length breadth Expected Output Actual Output
1 2 2 4 4
2 3 5 15 8
Output:
Mutant M5:
Test Cases length breadth Expected Output Actual Output
1 2 2 4 4
2 3 5 15 15
Output:
Calculation of Mutation Score:
No. of killed mutants = 3
No. of live mutants = 2
Total no. of mutants = 5
Mutation Score = No. of killed mutants / Total no. of mutants
= 3/5
= 0.6
Discussion: Mutation testing is a structural testing technique, which uses the structure of the
code to guide the testing process. On a very high level, it is the process of rewriting the
source code in small ways in order to remove the redundancies in the source code
These ambiguities might cause failures in the software if not fixed and can easily pass
through testing phase undetected. Mutation testing helps us in assessing the effectiveness of
the test suite. It helps us in identifying which of the test suite is effective in finding errors in
the program.
Learning: The purpose of mutation testing is not only to assess the capability of a test suite
but also to enhance the test suite. Therefore it is effective for selecting test suite for a required
program.
Experiment No: 10
Aim
Write a program to generate test cases using cause effect graph technique.
Theory
This technique is a popular technique for small programs and considers the combinations of
various inputs which were not available in earlier discussed techniques like boundary value
analysis and equivalence class testing. Such techniques do not allow combinations of inputs
and consider all inputs as independent inputs. Two new terms are used here and these are
causes and effects, which are nothing but inputs and outputs respectively.
FlowChart
Program
#include<iostream>
#include<conio.h>
}
string c[6],e[5];
void generateCauseEffect()
{
c[0]="a < b+c";
c[1]="b < a+c";
c[2]="c < a+b";
c[3]="a^2 = b^2 + c^2";
c[4]="a^2 > b^2 + c^2";
c[5]="a^2 < b^2 + c^2";
e[0]="Invalid Triangle";
e[1]="Right Angle Triangle";
e[2]="Obtuse Angle Triangle";
e[3]="Acute Angle Triangle";
e[4]="Impossible";
}
void causeEffectTesting(int min[3],int max[3])
{
int expOut[6],i=0,x=0,y=0,z=0,testCases[6][3];
testCases[0][0]=3;
testCases[0][1]=2;
testCases[0][2]=1;
testCases[1][0]=2;
testCases[1][1]=3;
testCases[1][2]=1;
testCases[2][0]=2;
testCases[2][1]=1;
testCases[2][2]=3;
testCases[3][0]=5;
testCases[3][1]=4;
testCases[3][2]=3;
testCases[4][0]=5;
testCases[4][1]=4;
testCases[4][2]=2;
testCases[5][0]=5;
testCases[5][1]=5;
testCases[5][2]=5;
cout<<"\n\nTest Cases...";
cout<<"\n+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+";
cout<<"\n|\t\t Input\t\t\t| Expected\t\t|";
cout<<"\n|\ta\t\tb\t\tc\t| Output\t\t|";
cout<<"\n+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+\n";
for(int i=0;i<6;i++)
{
for(int j=0;j<3;j++)
{
if(j==0) cout<<"|";
cout<<"\t"<<testCases[i][j]<<"\t|";
}
triangle(testCases[i],min,max);
if(expOut<0)
cout<<" Invalid input\t";
else
{
int k;
for(int i=0;i<5;i++)
{
if(effect[i]==1)
{
k=i;
break;
}
}
cout<<" "<<e[k];
}
cout<<"\t|"<<endl;
}
cout<<"+"; for(int i=0;i<71;i++) cout<<"-"; cout<<"+";
}
void decisionTable()
{
generateCauseEffect();
cout<<"\nCauses...";
for(int i=0;i<6;i++)
{
cout<<"\nc"<<i+1<<":"<<c[i];
}
cout<<"\nEffects...";
for(int i=0;i<5;i++)
{
cout<<"\ne"<<i+1<<":"<<e[i];
}
cout<<"\n\nDecision Table\n";
cout<<"Conditions\n--------------------------------------------------------------------------\n";
cout<<"c1:"<<c[0]<<"\t\t\t0 1 1 1 1 1 1 1 1 1 1\n";
cout<<"c2:"<<c[1]<<"\t\t\tX 0 1 1 1 1 1 1 1 1 1\n";
cout<<"c3:"<<c[2]<<"\t\t\tX X 0 1 1 1 1 1 1 1 1\n";
cout<<"c4:"<<c[3]<<"\t\tX X X 1 1 1 1 0 0 0 0\n";
cout<<"c5:"<<c[4]<<"\t\tX X X 1 1 0 0 1 1 0 0\n";
cout<<"c6:"<<c[5]<<"\t\tX X X 1 0 1 0 1 0 1 0\n";
cout<<"--------------------------------------------------------------------------\n";
cout<<"e1:"<<e[0]<<"\t\t1 1 1\n";
cout<<"e2:"<<e[1]<<"\t 1\n";
cout<<"e3:"<<e[2]<<"\t 1\n";
cout<<"e4:"<<e[3]<<"\t 1\n";
cout<<"e5:"<<e[4]<<"\t\t\t 1 1 1 1 1\n";
cout<<"\n\n";
}
int main()
{
int min[3],max[3];
for(int i=0;i<3;i++)
{
cout<<"Enter min and max value of vertex"<<i+1<<":";
cin>>min[i]>>max[i];
}
decisionTable();
causeEffectTesting(min,max);
}
Output
Related Theory :
We have to execute the static analysis tool to analsye its functioning. Here we consider
cppcheck as the static analysis tool .
Introduction:
Cppcheck is an analysis tool for C/C++ code. Unlike C/C++ compilers and many other
analysis tools, it doesn’t detect syntax errors. Cppcheck only detects the types of bugs that the
compilers normally fail to detect. The goal is no false positives.
• You can check non-standard code that includes various compiler extensions, inline
assembly code, etc.
• Cppcheck should be compilable by any C++ compiler that handles the latest C++ standard.
• Cppcheck should work on any platform that has sufficient CPU and memory.
Algorithm :
Program 1:
#include<iostream>
using namespace std;
int main(){
int a[10];
for(int i=10;i>0;i++)
cin>>a[i];
return 0;
}
Output :
Program 2:
/*Program to compute day of the week*/
/*Header Files*/
#include<stdio.h>
#include<conio.h>
void main()
{
int day,month,year,century,Y,y1,M,date,validDate=0,leap=0;
//clrscr();
printf("Enter day:");
scanf("%d",&day);
printf("Enter month:");
scanf("%d",&month);
printf("Enter year (between 1900 and 2058):");
scan("%d",&year);
/*Check whether the date is valid or not*/
if(year>=1900&&year<=2058) {
if(year%4==0) { /*Check for leap year*/
leap=1;
if((year%100)==0&&(year%400)!=0) {
leap=0;
}
}
if(month==4||month==6||month==9||month==11){
if(day>=1&&day<=30) {
validDate=1;
}
else {
validDate=0;
}
}
else if(month==2){
if(leap==1&&(day>=1&&day<=29)) {
validDate=1;
}
else if(day>=1&&day<=28) {
validDate=1;
}
else {
validDate=0;
}
}
else if((month>=1&&month<=12)&&(day>=1&&day<=31)){
validDate=1;
}
else {
validDate=0;
}
}
if(validDate) { /*Calculation of Day in the week*/
if(year>=1900&&year<2000){
century=0;
y1=year-1900;
}
else {
century=6;
y1=year-2000;
}
Y=y1+(y1/4);
if(month==1) {
if(leap==0) {
M=0; /*for non-leap year*/
}
else {
M=6; /*for leap year*/
}
}
else if(month==2){
if(leap==0) {
M=3; /*for non-leap year*/
}
else {
M=2; //for leap year
}
}
else if((month==3)||(month==11)) {
M=3;
}
else if((month==4)||(month==7)) {
M=6;
}
else if(month==5) {
M=1;
}
else if(month==6) {
M=4;
}
else if(month==8) {
M=2;
}
else if((month==9)||(month==12)) {
M=5;
}
else {
M=0;
}
date=(century+Y+M+day)%7;
if(date==0) { /*Determine the day of the week*/
printf("Day of the week for [%d:%d:%d] is Sunday",day,month,year);
}
else if(date==1) {
printf("Day of the week for [%d:%d:%d] is Monday",day,month,year);
}
else if(date==2) {
printf("Day of the week for [%d:%d:%d] is Tuesday",day,month,year);
}
else if(date==3) {
printf("Day of the week for [%d:%d:%d] is Wednesday",day,month,year);
}
else if(date==4) {
printf("Day of the week for [%d:%d:%d] is Thursday",day,month,year);
}
else if(date==5) {
printf("Day of the week for [%d:%d:%d] is Friday",day,month,year);
}
else {
printf("Day of the week for [%d:%d:%d] is Saturday",day,month,year);
}
}
else {
printf("The date entered [%d:%d:%d] is invalid",day,month,year);
}
getch();
}
Output :
Program 3:
#include <stdio.h>
#include<iostream.h>
int *p;
int a(int sz)
{
delete [] p;
if (sz <= 0)
throw std::runtime_error("size <= 0");
p = new int[sz];
}
Output :
Program 4:
int main(void)
{
char *str1 = "abcdefghijklmnop";
char *str2 = malloc(100);
strcpy(str2, str1);
char x[16];
x[16] = 10;
return 0;
}
Output :
Program 5:
/*Program to classify whether a triangle is acute, obtuse or right angled given the sides of
the triangle*/
//Header Files
#include<stdio.h>
#include<conio.h>
#include<math.h>
void main() //Main Begins
{
double a,b,c;
double a1,a2,a3;
int valid=0;
clrscr();
printf("Enter first side of the triangle:"); /*Enter the sides of Triangle*/
sprintf("%lf",&a);
printf("Enter second side of the triangle:");
scanf("%lf",&b);
printf("Enter third side of the triangle:");
scanf("%lf",&c);
/*Checks whether a triangle is valid or not*/
if(a>0&&a<=100&&b>0&&b<=100&&c>0&&c<=100) {
if((a+b)>c&&(b+c)>a&&(c+a)>b) {
valid=1;
}
else {
valid=-1;
}
}
if(valid==-1) {
a1=(a*a+b*b)/(0);
a2=(b*b+c*c)/(a*a);
a3=(c*c+a*a)/(b*b);
if(a1<1||a2<1||a3<1) {
printf("Obtuse angled triangle");
}
else if(a1==1||a2==1||a3==1) {
printf("Right angled triangle");
}
else {
printf("Acute angled triangle");
}
}
else if(valid==-1) {
printf("\nInvalid Triangle");
}
else {
printf("\nInput Values are Out of Range");
}
getch();
}
Output :
Discussion :
As we executed the programs we noted that the syntax errors were not detected by this tool
which was according to what the tool did.
The bugs other that syntax errors were detected by the tool and it also showed us some
improvements which can be made in the code like reducing the scope of the variables
according to its usage.
Hence this can be used for finding bugs.
There are limits of Cppcheck. Cppcheck is rarely wrong about reported errors. But there are
many bugs that it doesn’t detect. More bugs can be found in the software by testing it
carefully, than by using Cppcheck.More bugs can be found in the software by instrumenting
the software, than by using Cppcheck. But Cppcheck can still detect some of the bugs that
one may miss when testing and instrumenting your software.
Experiment No: 12
Aim
Write a program for generating Control Flow Graph and DD Path Graph
Theory
What is Control Flow Path?
A control flow path is a graphical representation of all paths that might be traversed through a
program during its execution. Most representations are of two types of blocks. Viz - An entry
block through which control enters into the flow graph and the exit block through which all
control flow leaves.
By this representation, we can also calculate cyclomatic complexity. It also enables us to
verify reachability, relationship with other nodes, edges and loops effectively.
PT–14
Flow Chart
Program
#include<iostream>
#include<fstream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
// constants
const int NOTYPE=0 ,
TYPE_REGULAR=1,TYPE_IF=2,TYPE_ELSE=3,TYPE_IF_CLOSE=4,TYPE_ELSE_CLOSE=5;
const string IF_STRING="if(", ELSE_STRING="else" , CLOSE_STRING="}";
class Node
{
public:
int num;
static int node_count;
return new_node;
}
};
int Node::node_count = 1;
class Stack_Node{
public:
Node* ptr;
int type;
Stack_Node(){
this->ptr = NULL;
this->type = NOTYPE;
}
stack<Stack_Node> mStack;
class Graph {
public:
Node* head;
void printGraph(){
queue<Node*> q;
q.push(head);
while(!q.empty()){
cout<<travel_ptr->num<<" : ";
if (g->head == NULL)
{
g->head = current_node;
Stack_Node new_node(current_node,TYPE_REGULAR);
mStack.push(new_node);
return;
}
// Pop ELSE
mStack.pop();
// Attach IF_CLOSE
mStack.top().ptr->next.push_back(current_node);
// Pop IF_CLOSE
mStack.pop();
// Pop IF
mStack.pop();
}
else {
Stack_Node new_node(current_node,TYPE_ELSE);
mStack.push(new_node);
}
if (mStack.top().type == TYPE_IF)
{
Stack_Node new_node(current_node,TYPE_IF_CLOSE);
mStack.push(new_node);
}else if(mStack.top().type == TYPE_ELSE){
Stack_Node new_node(current_node,TYPE_ELSE_CLOSE);
mStack.push(new_node);
}
}
}
// if the line is a regular line we see if the top is a regular line and pop it and push this line
// else if the top node is IF_TYPE or ELSE_TYPE we simply push it
else {
if (mStack.top().type == TYPE_REGULAR){
mStack.pop();
Stack_Node new_node(current_node,TYPE_REGULAR);
mStack.push(new_node);
}
else if(mStack.top().type == TYPE_IF || mStack.top().type == TYPE_ELSE){
Stack_Node new_node(current_node,TYPE_REGULAR);
mStack.push(new_node);
}
}
}
ifstream file(filename);
if (file.is_open())
{
// start reading the file
while(getline(file,line))
{
process_line(line,g);
}
}
g->printGraph();
return 0;
}
Output
Sample.txt
Output Graph
Discussion
Graphs that represent the control flow of programs have been studied since many years and are known
under the names of control flow graphs or program graphs. There are mainly two types of such
graphs: one that associates one node with each statement in programs, see, for example, where control
flow graphs are applied to optimization or for the application in software engineering; and the other
that replaces maximal sets of consecutive nodes with a single entry and a single exit called blocks or
segments, by single nodes. Blocks can be derived from the control flow graphs of the first type or
constructed directly from the programs. Both types capture the control flow by abstraction from the
program details.
Since the control flow through programs is determined by the decisions, for example, the if-then-else-
constructs, based on the data and the conditions in such constructs, it is promising to keep in graphical
representations of programs only the decisions and the control flow between them and thus defining a
reduction of control flow graphs that preserves the branching structure.
Related Theory :
Graph Matrices :
A graph matrix is a square matrix with one row and one column for every node of the graph.
The size of the matrix (number of rows and number of columns) is the number of nodes of
the
graph. Graph matrix is the tabular representation of a program graph. An example of program
graph and its graph matrix is given below :
If we assign weight for every entry in the table, then this may be used for the identification of
independent paths. The simplest weight is 1, if there is a connection and 0 if there is no
connection. Each row with more than one entry represents a predicate node and cyclomatic
complexity is predicate nodes plus one ( +1).
Source Code :
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
class vertex{
public:
int in_deg;
vector<int> e_list;
int line_num;
vertex(){
in_deg = 0;
}
vertex(const vertex & v){
in_deg = v.in_deg;
for (int i=0;i<v.e_list.size();i++)
e_list.push_back(v.e_list[i]);
line_num = v.line_num;
}
};
struct graph{
int num_vertex;
vector<vertex> v_list;
graph(){
num_vertex = 0;
}
};
int buildDDHelper(graph & dd, int index, int & deleted){
int i,prev_val;
for (i=index;i<dd.num_vertex;){
if (dd.v_list[i].in_deg == 1 &&
dd.v_list[i].e_list.size() == 1){
prev_val = i;
i++;
i = val;
}
dd.v_list[prev_val].e_list[0] = i + deleted + 1;
}
return i;
}
void buildDD(graph & dd){
int deleted = 0;
string str(ar);
int pos = str.find_first_not_of(' ');
if (pos == string::npos)
continue;
p1:
cfg.v_list[prev_pos].e_list.push_back(cfg.num_vertex);
buildCFG(cfg,fin);
if (pos == string::npos)
continue;
if (str.substr(pos,4) == "else"){
vertex v;
v.in_deg = 1;
cfg.insert(v);
cfg.v_list[prev_pos + 1].e_list.push_back(cfg.num_vertex);
buildCFG(cfg,fin);
do{
fin.getline(ar,100);
string str(ar);
int pos = str.find_first_not_of(' ');
if (pos == string::npos)
continue;
vertex v2;
v2.in_deg = 2;
cfg.insert(v2);
cfg.v_list[cfg.num_vertex -2].e_list.push_back(cfg.num_vertex);
cfg.v_list[prev_pos2].e_list.push_back(cfg.num_vertex);
if (str[pos] == '}')
return;
break;
}
while (true);
}
else{
vertex v2;
v2.in_deg = 2;
cfg.insert(v2);
cfg.v_list[cfg.num_vertex -2].e_list.push_back(cfg.num_vertex);
cfg.v_list[prev_pos2 - 2].e_list.push_back(cfg.num_vertex);
if (str.substr(pos,2) == "if")
goto p1;
else if (str[pos] == '}')
return;
}
break;
}
while (true);
else{
vertex v;
v.in_deg = 1;
cfg.insert(v);
cfg.v_list[prev_pos].e_list.push_back(cfg.num_vertex);
if (str[pos] == '}')
break;
}
}while (true);
}
for(i=0;i<size;i++){
if(i==0)
printf(" %d ",ar1[i]);
else
printf("%d ",ar1[i]);
}
int count=2;
printf("\n");
for(i=0;i<size;i++){
printf("%d ",ar1[i]);
for(j=0;j<size;j++){
printf("%d ",arr[i][j]);
if(arr[i][j]==1)
count++;
}
count--;
printf("\n");
}
printf("\n Number of Independent Path : %d",count);
}
int main(){
char file_name[100];
if (!fin){
cerr << "FILE NOT FOUND!\n";
}
else{
char ar[100];
bool terminate = false;
while (!terminate){
fin.getline(ar,100 - 1);
string str(ar);
int pos = str.find_first_not_of(' ');
if (str[pos] == '#')
continue;
if (str.find("main()") != string::npos){
graph cfg;
vertex v;
cfg.insert(v);
buildCFG(cfg,fin);
graph_matrix(dd);
terminate = true;
}
}
}
Output :
Discussion :
Graph matrices is a mathematical concept which is used in software testing to trace all possible paths in a
graph, at least once, and calculates the cyclomatic complexity. It converts a flow graph into matrix and each
node corresponds to particular row & column, by applying step by step process to get desired paths with its
calculated values. It represents a crucial relationship between nodes. It represents complex graphs in matrix
form, and applies multiple operations in matrices to trace a path. Each node in a graph holds particular value.
Sometimes, path can be traced twice which occurs redundancy or can be missed. These misconception and
tracing errors generate interruptions and leads us to confusion.
Learning and Finding :
As we know, each graph matrix expresses a direct link between nodes. If we take the square
of the matrix, it shows 2-links relationships via one intermediate node. Hence, square matrix
represents all paths of two links long. The Kth power of matrix represents all paths of K links
long.
Program No: 14
Aim: Security Testing of a Website
Theory :
Security is the procedure used to protect information from various threats. It is very important
to protect sensitive and critical information and data while communicating over the network.
The user wants implementation of a safeguard to protect personal, sensitive and financial
information. We want data to be accurate, reliable and protected against unauthorized access.
(v) Reliability: What is the frequency of a failure? How much time does the network take
to recover from a failure? What measures are taken to counter catastrophic failure?
(vi) Non-repudiation: Is the receiver able to prove that the data received came from a
specific sender?
A web application must fulfil the above mentioned primary security requirements. Testing
the threats and vulnerabilities in a web application is an important activity. The tester must
Algorithm:
S. Description Yes/
No No
Authentication, Access control and privacy
7 Does the web server lock the users who try to access the website multiple Yes
times with invalid login ids/passwords?
8 Have you tested the combinations of invalid and valid login ids/passwords? Yes
Firewall
11 Does the firewall properly implement all the security policies of the Yes
company?
12 Are firewalls’ adequacy tested? Yes
13 Is the ‘security in charge’ aware of the known faults in the firewalls Yes
Data security
Encryption
23 Is sensitive and critical information (such as password, credit card number, Yes
company’s financial sheets) encrypted?
24 Is Security Socket Layer (SSL) used to provide encryption of sensitive Yes
elements?
25 Does the enforcement of encryption standard affect the speed of the web Yes
page?
Virus
29 Are viruses sent for analysis to the anti-virus software company? Yes
30 Are users trained for virus prevention and recovery procedures? Yes
38 In case of site crash, is there any provision to route to another server? Yes
39 Have recovery mechanisms been defined and tested? Yes
40 Is any criteria followed to ensure the completion and correction of recovery Yes
procedures?
Yes
General
41 Are any warning messages issued when the user enters or leaves the secured Yes
website?
42 Is the user allowed to login with the same account from different machines Yes
(simultaneously)?
43 Are unauthorised external sites identified and screened? Yes
44 Can the previous page be accessed after signing out from the website? Yes
45 Is the user able to access restricted pages after the session is timed out? Yes
Result: It is necessary to perform Security Testing before the deployment of the website to
maintain its integrity. Thus it should be taken care to perform security testing to protect the
website from the attack of malicious users.
Program No: 15
Aim: Generate test cases for a shopping website using compatibility, navigational form
based ,usability and load testing techniques.
Theory: The main challenge of testing a web application is not only to find common
software errors, but also to test associated quality related risks that are specific to a web
application. We should know the architecture and key areas of web application to effectively
plan and execute the testing. Web applications are difficult and complex as compared to
traditional client-server applications. They are required to be tested on different browsers and
varied platforms. It is important and critical to identify the areas that need special focus while
testing a web application. A web application needs to be tested for:
(i) Functionality
(ii) Usability
(iv) Security
There are numerous issues that need to be considered that are specific to web application,
hence, only a sub-set of conventional testing techniques are applicable for testing a web
application.
Test Cases:
Compatibility testing:
Form X X X X X
Navigational testing:
Load testing:
Usability testing :
Introduction
1. Do all the labels and yes
hyperlinks display a
meaningful title
2. Does any internal or external No
link provide incorrect details
3. Are appropriate error yes
messages displayed
wherever required
4. Do you frequently encounter Less
problems in the application
or not
Efficiency
5. How easily are features and Easy
updates downloadable
6. How easily are common task Very easy
performed
7. How frequently are errors Less
encountered while
navigating through the web
application
8. Is the response time a cause No
of frustration
9. How many pages are <3
accessed to complete a Task
10. How quickly does the web Fast
app recover from an error
Completeness
11. Are some features missing No
from the web app?
12. Are any additional control No
required
13. To what degree are you High
satisfied with web app
14. Are web pages well Yes
designed
15. Does the online help and Yes
documentation provide
enough info
Learnability
16. How easily is the user able Very easy
to learn the features of the
web page
17. Are the links menus and lists Very easy
are easily understandable
18. How often is the search High
feature used
19. How easily is it to return to Very easy
homepage
20. Can the features be used Yes
without any help and
assistance
Clarity and accuracy of
online and written
documentation
21 Is the terminology well Yes
understood
22. How easily are the topics Easy
found in the online help
23. Are the topic content No
sufficient in the help
24. How frequently was the Less
required topic found in the
help
25. Is online help useful when Yes
error message are
encountered
26. Do menus lists hyperlinks Yes
toolbars and tables operate
as stated
27. Are the steps to complete a Yes
task correctly stated
28. How helpful is the content Very useful
related to topics
General
6. SELENIUM
Selenium is a set of different software tools each with a different approach to supporting test
automation. Most Selenium QA Engineers focus on the one or two tools that most meet the
needs of their project, however learning all the tools will give you many different options for
approaching different test automation problems. The entire suite of tools results in a rich set
of testing functions specifically geared to the needs of testing of web applications of all types.
These operations are highly flexible, allowing many options for locating UI elements and
comparing expected test results against actual application behavior. One of Selenium’s key
features is the support for executing one’s tests on multiple browser platforms.
Selenium first came to life in 2004 when Jason Huggins was testing an internal application at
ThoughtWorks. Being a smart guy, he realized there were better uses of his time than
manually stepping through the same tests with every change he made. He developed a
Javascript library that could drive interactions with the page, allowing him to automatically
rerun tests against multiple browsers. That library eventually became Selenium Core, which
underlies all the functionality of Selenium Remote Control (RC) and Selenium IDE.
Selenium RC was ground-breaking because no other product allowed you to control a
browser from a language of your choice.
Advantages:
Disadvantages:
1. Selenium needs very much expertise resources. The resource should also be very well
versed in framework architecture.
2. Selenium only supports web based application and does not support windows based
application.
3. It is difficult to test Image based application.
4. Selenium need outside support for report generation activity like dependence on TestNG or
Jenkins.
5. Selenium does not support built in add-ins support.
6. Selenium user lacks online support for the problems they face.
7. Selenium does not provide any built in IDE for script generation and it need other IDE like
Eclipse for writing scripts.
8. Selenium Automation Engineers are bit in scarcity these days.
9. Selenium script creation time is bit high.
10. Selenium does not support file upload facility.
11. Selenium partially supports for Dialog boxes.
7. FitNesse:
FitNesse is a web server, a wiki and an automated testing tool for software. It is based
on Ward Cunningham's Framework for Integrated Test and is designed to support acceptance
testing rather than unit testing in that it facilitates detailed readable description of system
function.
FitNesse allows users of a developed system to enter specially formatted input (its format is
accessible to non-programmers). This input is interpreted and tests are created automatically.
These tests are then executed by the system and output is returned to the user. The advantage
of this approach is very fast feedback from users. The developer of the system to be tested
needs to provide some support (classes named "fixtures", conforming to certain conventions).
FitNesse is written in Java (by Robert C. Martin and others). The program first supported
only Java, but versions for several other languages have been added over time (C++, Python,
Ruby, Delphi, C#, etc.).
FitNesse as a testing tool
Tests are described in Fitnesse as some sort of coupling of inputs and expected output. These
couplings are expressed as some sort of variation of a decision table. The FitNesse tool
supports several of these variations, ranging from literal decision tables to tables that execute
queries to tables that express testing scripts (i.e. a literal ordering of steps that must be
followed to reach a result). The most generic form is a fully free-form table that can be
interpreted in any way the test designers like. All tests are expressed in the shape of some sort
of table, however.
FitNesse is focused entirely on easily creating tests, allowing testers and developers to focus
on creating high-quality tests rather than getting lost in the mechanics of executing a test.
Given the way FitNesse works, creating tests easily involves three factors:
Advantages:
FitNesse tests can give us feature feedback very early in the project. In fact, the tests
ought to be written first, so programmers can code to the tests.
FitNesse tests can give us feature feedback very frequently. They can be run
manually or automatically by anyone with web access to the server, as frequently as required.
Every week, every day, every hour in a crunch.
FitNesse tests are deterministic: they either run green or red. If they run green for a
given requirement, either the requirement is done and we move on to others, or the set of tests
is not yet exactly right, in which case we refine them. Either way, we are successively
refining the system in an orderly way. With each new test running green, we can all see the
system getting better, more valuable, closer to what we need.
Being based on example data, FitNesse tests exercise more paths through the
business logic. When you use FitNesse, you run less risk of missing important features or
functional behavior.
Disadvantages
Functionality
Watir project consists of several smaller projects. The most important ones are watir-classic,
watir-webdriver and watirspec.
Watir-classic
Watir-classic makes use of the fact that Ruby has built in Object Linking and
Embedding (OLE) capabilities. As such it is possible to drive Internet Explorer
programmatically. Watir-classic operates differently than HTTP based test tools, which
operate by simulating a browser. Instead Watir-classic directly drives the browser through the
OLE protocol, which is implemented over the Component Object Model (COM) architecture.
The COM permits interprocess communication (such as between Ruby and Internet Explorer)
and dynamic object creation and manipulation (which is what the Ruby program does to the
Internet Explorer). Microsoft calls this OLE automation, and calls the manipulating program
an automation controller. Technically, the Internet Explorer process is the server and serves
the automation objects, exposing their methods; while the Ruby program then becomes the
client which manipulates the automation objects.
Pros:
1. No external server needed to run a test. Just run "ruby test.rb" and it goes, this
simplifies any scripts you use to execute your tests through continuous integration
servers like Hudson or Bamboo or whatever.
2. GREAT API! this is the kind of API rubyists dream of... its easy and fun and once you
get the basics you can just guess how to write the code without needed alot of
documentation.
3. Tests can often be run through alternative tools... watir-webdriver, webrat, celerity and
more all more-or-less support the watir API. This means that if you write your
framework carefully you are not locked in to watir... its hard to overstate the value of
this.
4. only works in ruby (not jruby), BUT watir-webdriver does work in both ruby and jruby
(see above point)
5. Havent really had much issue dealing with AJAX, watir always seems to know when the
page is "loaded"
6. Typically runs faster than a comparable selenium test.
7. Better support for headless browsers, see Celerity.
Cons:
6. Each browser's implementation is a little bit different, and the driver for each browser is
written independently. This can mean inconsistent test results between browsers, but it
hasnt really been much an issue for me.
7. api is ruby only (but if you use ruby, this is actually an advantage because you get a
truly ruby-esque tool)
8. doesnt seem to be as widely used as selenium, a lil bit harder to find engineers (finding
good ones is pretty much impossible)
1. QTP has record and playback facility with editing the scripts generated after the recording
is completed. It supports different recording mode i.e. Normal, Analog and Low level which
facilitates to automate different types of applications.
2. QTP supports almost all popular automation frameworks like Linear, Keyword, Data
Driven, Hybrid etc. for automation purpose.
3. QTP is very much user friendly and anyone can start using it very less expertise.
4. QTP has built in IDE associated which is very much easy to use.
5. Its primary scripting language is VB script, thus it does need skilled code to start
automation work. Also this tool is packed with many add-ons which supports it very much.
6. QTP can be integrated with one of the most useful test management tool i.e. Quality Center
(QC). This integration very much supports the testing activity.
7. One of the most important features of QTP is Object Repository which is useful to store
the property of the objects which is being tested.
8. QTP has very excellent Object Identification process.
9. QTP also supports Object Spy mechanism to identify object properties associated.
10. QTP supports many add-ins like Peoplesoft, Java, .Net, Siebel, Visual Basic, Web
Service, SAP, ActiveX, Silver Light, Java etc.
11. QTP supports built in Database testing. It uses excel or Data tables in very expertise
manner.
12. QTP provides very good parameterization, navigation, script edition, result validation etc.
features.
13. QTP has excellent support for reporting to show the test result.
14. QTP also provide online support for their users.
Disadvantages:
1. QTP is used with license and the license cost of using it is very high.
2. For getting online support we need to keep on the license renewal.
3. QTP supports many add-ins, but for using it we need to buy it.
4. QTP takes loads of RAM and CPU and comparatively slow in execution.
c. WebLOAD
WebLOAD is load testing tool, performance testing, stress test web applications. This web
and mobile load testing and analysis tool is from RadView Software. Load testing tool
WebLOAD combines performance, scalability, and integrity as a single process for the
verification of web and mobile applications. It can simulate hundreds of thousands of
concurrent users making it possible to test large loads and report bottlenecks, constraints, and
weak points within an application.
Using its multi-protocol support, WebLOAD simulates traffic of hundreds of thousands of
users and provides analysis data on how the application behaves under load. WebLOAD
monitors and incorporates statistics from the various components of the system under test:
servers, application server, database, network, load-balancer, firewall, etc., and can also
monitor the End User Experience and Service Level Agreement (SLA) compliance in
production environments.
WebLOAD's features include:
6. IDE An integrated development environment for visually recording, editing & debugging
load test scripts. WebLOAD's proxy-based recorder records HTTP activity. Test are
generated in JavaScript and can be enhanced and edited using various tools in the IDE.[7]
7. Correlation Automatic correlation of dynamic values such as Session IDs, enables a
script to be executed dynamically with multiple virtual clients.
8. Load Generation WebLOAD generates load from on-premises machines or from the
cloud.[10]
9. Analytics A set of predefined analysis reports provides performance data, helping users
identify bottlenecks. Reports and analysis data can also be viewed remotely via a
customizable Web Dashboard.[11]
10. PMM Collects server-side statistics during test runs, providing users with additional data
for root-cause analysis.
11. Web Dashboard Analyzing performance test results from any browser or Mobile device.
Advantages:
1) Some developers complain that Web Load is not an open source in true sense.
Contrary to what their press release and website says, it contains proprietary
components that are released in binary form with no source code
d)HP Unified Functional Testing
HP Unified Functional Testing (UFT) software, formerly known as HP QuickTest
Professional (QTP), provides functional and regression test automation for software
applications and environments. HP Unified Functional Testing can be used for enterprise
quality assurance.
HP Unified Functional Testing supports keyword and scripting interfaces and features a
graphical user interface. It uses the Visual Basic Scripting Edition (VBScript) scripting
language to specify a test procedure, and to manipulate the objects and controls of the
application under test.
HP Unified Functional Testing was originally written by Mercury Interactive and called
QuickTest Professional. Mercury Interactive was subsequently acquired by Hewlett
Packard(HP) in 2006. HP Unified Functional Testing 11.5 combined HP QuickTest
Professional and HP Service Test into a single software package, which is currently available
from the HP Software Division. The integrated HP Unified Functional Testing software
allows developers to test from a single console all three layers of a program's operations: the
interface, the service layer and the database layer.
Advantages:
1. QTP has record and playback facility with editing the scripts generated after the recording
is completed. It supports different recording mode i.e. Normal, Analog and Low level which
facilitates to automate different types of applications.
2. QTP supports almost all popular automation frameworks like Linear, Keyword, Data
Driven, Hybrid etc. for automation purpose.
3. QTP is very much user friendly and anyone can start using it very less expertise.
4. QTP has built in IDE associated which is very much easy to use.
5. Its primary scripting language is VB script, thus it does need skilled code to start
automation work. Also this tool is packed with many add-ins which supports it very much.
6. QTP can be integrated with one of the most useful test management tool i.e. Quality Center
(QC). This integration very much supports the testing activity.
7. One of the most important features of QTP is Object Repository which is useful to store
the property of the objects which is being tested.
8. QTP has very excellent Object Identification process.
9. QTP also supports Object Spy mechanism to identify object properties associated.
10. QTP supports many add-ins like Peoplesoft, Java, .Net, Siebel, Visual Basic, Web
Service, SAP, ActiveX, Silver Light, Java etc.
11. QTP supports built in Database testing. It uses excel or Data tables in very expertise
manner.
12. QTP provides very good parameterization, navigation, script edition, result validation etc.
features.
13. QTP has excellent support for reporting to show the test result.
14. QTP also provide online support for their users.
Disadvantages:
1. QTP is used with license and the license cost of using it is very high.
2. For getting online support we need to keep on the license renewal.
3. QTP supports many add-ins, but for using it we need to buy it.
4. QTP takes loads of RAM and CPU and comparatively slow in execution.
5. QTP supports limited version of browsers for script execution
6. QTP does not support multithreading i.e. running multiple instance of script in different
browsers at same time.
DELHI TECHNOLOGICAL
UNIVERSITY