Steps duplication. Get rid of Copy/Paste
@ Tu, 23 September, 18:30Every so often during tests automation we may encounter steps description like "Repeat steps 1-12" or even more interesting like instructions to repeat all steps using some specific settings. It means that we have to duplicate some code during automated tests development. But excessive code duplication may cause a number of maintainance problems. Any changes have to be made in all repetitive code occurences. And this makes additional time loss. It can be avoided in the following ways:
- Repetitive code wrapping by separated modules (functions) - quite reasonable solution corresponding to the idea of modules/functions usage. Also such approach may lead us to functional library creation which can be simply used in any similar automation project ( it depends on modules/functions abstraction from specific application under test ). Let's describe this solution using some simple example. There is some code fragment (example is written on JScript for TestComplete but it can be expanded to any other similar solutions by analogy):
function SomeTest() {
// Step 1
// Print "Step 1"
Log.Message( "Step 1" );
// Step 2
// Print "Step 2"
Log.Message( "Step 2" );
// Step 3
// Repeat steps 1-2
Log.Message( "Step 1" );
Log.Message( "Step 2" );
}
This is very simple example but it is enough to demonstrate the idea. Instead of regular output writing there may be complex constructions but approach is still the same. This code fragment may be re-written to be more stable to possible modifications. We may take spep 1 and step 2 code into separate functions:
function Step1() {
Log.Message( "Step 1" );
}
function Step2() {
Log.Message( "Step 2" );
}
function SomeTest() {
// Step 1
// Print "Step 1"
Step1();
// Step 2
// Print "Step 2"
Step2();
// Step 3
// Repeat steps 1-2
Step1();
Step2();
}
In such cases when some series of steps is involved we may redesign code in the following way:
function Steps_1_2() {
Log.Message( "Step 1" );
Log.Message( "Step 2" );
}
function SomeTest() {
// Step 1
// Print "Step 1"
Steps_1_2();
// Step 2
// Print "Step 2"
// *** see above
// Step 3
// Repeat steps 1-2
Steps_1_2();
}
It means that we've implemented repetitive steps in separate function and used it in first step. Other steps implementation is skipped as already performed in the first step. The only thing made is comment is left informing that required actions were performed before. Such approach is suitable to apply in case series of repetitive steps is used many times with different parameters. But there are some situations when steps structure shouldn't be corrupted. As a rule, it's mostly up to strict requirements related to test structure in order to provide readability. In this case we have to keep some balance between possible solutions.
Such solutions have the following advantages:
- Huge code fragment duplication is minimized
- Code is shortened. Single function call usually require less code than multiple code fragment duplication.
Disadvantages:
- It doesn't make big advantage for small duplicated code parts
- It decreases readability due to necessity of making switch to separate functions in order to achieve required implementation part
- It corrupts test structure in case some huge code covering several steps is taken into single function
- Little duplications may cause helper functions amount increasing. If there are a lot of helper functions there is high possibility to get lost in this variety of code modules
Of course, these disadvantages may be avoided by means additional limititions, some conventions. But in some cases there are more flexible solutions - Driver-function usage. The idea is to create some helper function storing all steps implementation and at the same moment it can execute steps in some range. The actual test function contains only loop executing driver-function in some sequence of step ranges. If we take the last example we may overwrite it in the following way:
function SomeTestDriver( iStart, iEnd, aParam ) {
var i;
for( i = iStart ; i <= iEnd ; i++ ) {
switch( i ) {
case 1:
// Step 1
// Print "Step 1"
Log.Message( "Step 1" );
break;
case 2:
// Step 2
// Print "Step 2"
Log.Message( "Step 2" );
break;
case 3:
// Step 3
// Repeat steps 1-2
SomeTestDriver( 1 , 2 , aParam );
break;
}
}
}
function SomeTest() {
SomeTestDriver( 1 , 3 , null );
}
Here aParam argument is some reserved variable to pass some additional values into driver-function in case some steps should be repeated using some specific options. Similar implementations can be done in many different ways. This is only one example to demonstate the idea on practical example.
Advantages:
- Code duplication is omitted effectively in case of huge number of steps repetition (especially overall test case has to be repeated several times )
- Test case structure is still clearly seen
- Driver function may be used in many other tests providing the advantages of repetitive code wrapping by separate functions.
Disadvantages:
- Test may be put into endless loop by mistake. Driver function is called recursively for repetitive steps. But in case recursive call covers the same step as caller then execution loop is never ends ( it's true up to a point because in this case stack overflow is much possible).
- Initially high nesting level ( loop with branching) for driving function makes more difficulties for code reading. Nesting level of 5 is quite acceptable and some tasks may be implemented using such level. But if we add 2 more nesting levels which are initially available then there will be some problems with readability
Anyway, code duplication can be avoided. We can optimize our code endlessly but we may have not enough time for this as well as such optimized code may become hard to read and maintain. Nevertheless, some typical tasks may be resolved in typical way.
You must be logged in in order to post comments
