Program Development. • This includes: • “testing a program” • “proving a program correct” • “debugging a program” • “testing a program” involves supplying data to the program and observing the results. It is carried out OUTSIDE the program (or procedure). • “proving a program correct” involves study of the code and input and output conditions. It is carried out INSIDE the program.
Thoroughness of Testing • A successful test establishes the presence of errors for one set of conditions. • The problem is to produce test data which exhibits all possible behaviours of the program being tested. • Black-Box Testing: • Equivalence partitioning. • Functional testing. • Mutation testing. • Glass-Box Testing: • Statement Testing. • Branch Testing. • Path Testing.
Equivalence Partitioning. • e.g. specifications for a database product state that it must be able to handle any number of records from 1 to 16,383. • This leads to 3 equivalence classes. • equivalence class 1: less than 1 record. • equivalence class 2: from 1 to 16383 records. • equivalence class 3: more than 16383 records. • This might be tested for 0, 1, 2, 732, 16382, 16383 and 16384 records. What results do you expect for each of these values ?
Example of Equivalence Partitioning. • Consider a program which classifies triangles. • Input: 3 integers (giving the lengths of the sides). • Determine: the type of the triangle e.g. • equilateral (output E) all sides equal. • isosceles (output I) 2 sides equal. • scalene (output S) no sides equal. • right-angled (output R). • add output N to indicate “non-triangle” • exactly 3 values are required, all integers and all >0.
Example Continued • Possible errors: • fewer than 3 values are read in. • one or more of the numbers non-integer. • one or more of the numbers < = 0. • longest side p > = sum of other 2 sides. • N indicates any of these errors. • Test cases are needed to check the program responds correctly to each of these situations.
Equivalence Partitioning • Less than 3 numbers input. • One, two or three numbers non-integer. • One, two or three numbers negative. • p > = sum of other two numbers. • Choose examples in each set and check that the program gives the expected result. • Assume that if the program is correct for one example in the class of input, it will be so for all members of that “equivalence class”. • Typically an input condition is either a numeric value, a range of values, a set of related values or a Boolean condition.
Guidelines for equivalence Classes. • input condition = range: 1 valid & 2 invalid classes. • input condition = value: 1 valid & 2 invalid classes. • input condition = member of a set: 1 valid & 1 invalid class. • input condition = Boolean: 1 valid & 1 invalid class. • Also have equivalence classes for output. • It has been noticed that more errors occur near the boundaries of these classes. Hence the development of “boundary value analysis”.
Boundary Value Analysis. • (a) input condition specifies range bounded by values a and b. Design test cases to use values a and b and values close to them (both greater and smaller). • (b) input condition specifies a number of values. Design test cases to use the maximum and minimum values and others close to them. • (c) apply these guidelines to output conditions. e.g if a table of values is one output, design test data to generate the maximum and minimum number of entries in the table. • (d) if internal program data structures have prescribed boundaries, ensure that the boundaries are tested.
Testing Principles. • 1: Design test cases with the object of uncovering errors in the software. • 2: Design tests systematically. Do not rely on intuition. • 3: Establish a testing strategy that begins at the module level. • 4: Record all testing results and save test cases for reapplication during software maintenance. • “Bugs lurk in corners and congregate at boundaries”.
Functional Testing • Functional testing focusses on the functionality of the program. • Each of the functions implemented in the module is identified. • Test data are defined to test each function separately. • At this stage, the internal workings of the module are not considered.
Mutation Testing. (not needed for your exercises). • 1: the program is run with one particular set of data. • 2: some parts of the program are altered and then the program is run again with the same set of data. • This is intended to test the adequacy of the testing procedures and test cases. • Set of “mutant operators” is defined e.g. • change an addition to subtraction. • exchange two variables. • add unity to an arithmetic expression. • change > to <
Glass Box Testing. • Statement Testing: ensure that every statement in the program is executed at least once. • Branch Testing: for every decision point in the program, ensure that each branch is chosen at least once. • Path Testing: Ensure that every distinct path through the program is executed at least once. • You will need to apply glass-box testing to your program segments and document the results.
Work for Next Week • Consider the modules in your current programming exercise, or the module issued as an example. • For each module, produce a detailed testing scheme, using both black-box and glass-box methods. • Hand in your documentation, program listing and testing strategy for ONE module. • Apply the testing strategy to all modules as part of the workshop.
Functional Cohesion. • A measure of the strength of the association of the elements within a module. • Functional Cohesion: every function within the module contributes directly to performing one single task. • This means that a module which performs exactly one action or achieves a single goal has functional cohesion. e.g. “push a value onto a stack” or “set a 4-letter code”. • Such a module can be fully tested, re-used in many contexts and is easy to maintain or extend.
Informational Cohesion. • All actions in the module refer to the same data structure. • There are several sections of code, all independent and each with exactly one entry point and one exit point. • Since the module contains several independent pieces of code, it does not have functional cohesion. However the fact that all the independent sections refer to the same data structure gives it informational cohesion.
Communicational Cohesion. • A module has communicational cohesion if it performs a series of actions - • These actions are related by the sequnce of steps to be followed by the program - • and, in addition, if these actions are all performed on the same data.
Procedural Cohesion. • A module has procedural cohesion if it performs a series of actions related by a sequence of steps in a program. • In such a module the processing elements must be related and they must be executed in a specific order. • This implies the presence of a strong control structure.
Temporal Cohesion. • A set of functions related in time. • e.g. an “initialisation” module to be executed at the begining of a program. • The only connection between these functions may be that they are carried out at the start of the program. • It may be possible to replace one initialisation module which has only temporal cohesion with 2 or 3 others which also have functional or informational cohesion as well. This will be much easier to understand, re-use or maintain.
Logical Cohesion. • This contains a set of logically related functions. • e.g. all input and output functions, all graphics, all operations relating to a particular file of data. • Problems arising from this: interface is difficult to understand, there is too much intertwining and hence it is difficult to re-use. • With logical cohesion, the actions are intertwined, whereas with informational cohesion each section of code is completely independent.
Coincidental Cohesion. • There is no significant relations ship between the component parts of the module. They are grouped together only by coincidence. • This is evidence of a lack of design in splitting the program into modules. e.g. if the first 20 statements are placed in one module, the next 20 in another and so on, it is unlikely that such an arbitrary division would correspond to any underlying program logic. • This should be avoided. Even if such modularity still works correctly (one must assume the division did not split a loop), it will be hard to understand or re-use.
Use of Cohesion. • Software of a reasonable size is likely to contain modules of several different levels of cohesion. • This is not important - the important thing is to strive for high cohesion (especially functional cohesion) wherever possible. • Give the procedure a meaningful name and write a sentence saying what it does. Functional cohesion will correspond to a short, simple sentence. • If this is a compound sentence, then it probably performs more than one function and may have procedural or communicational cohesion. • If it contains words relating to time, then it probably has procedural or temporal cohesion.
Coupling. • Coupling is the interdependence between software modules. • Cohesion was the degree of interaction within a module and is something we wish to maximise. • Coupling is the degree of interaction between modules and is something we wish to minimise. • The higher the degree of coupling, the more likely is the “ripple effect” where changes inside one module affect the proper fucntioning of another module. • It also makes the module impossible to understand in isolation - we must have listings of both modules to understand what is going on.