Usefunctional programming in Perl to make test automation code more structural

Abstract

In USD test automation, we used Perl as the majorprogramming language. With daily practice, we noticed that as a programminglanguage, Perl does not enforce any particular programming paradigm(procedural, object-oriented, functional, or others) or even require theprogrammer to choose among them. Especially in software test automationscenario, we found there are many places we can use functional programming paradigmin Perl to enhance code structure and handle complex data with separated anddynamic logics. In this paper, we will discuss the basics of functionalprogramming paradigm, and then gives some real examples about how we use it inour test automation code.

What is functionalprogramming?

In computer science, functional programming is aprogramming paradigm that treats computation as the evaluation of mathematicalfunctions and avoids state and mutable data. It emphasizes the application offunctions, in contrast to the imperative programming style, which emphasizeschanges in state. [1]

To better understand functional programming concept,we need to look at 2 other kinds of terminology: higher-order function andfirst-class function.

Higher-order functions are functions that can eithertake other functions as arguments or return them as results. In calculus, anexample of a higher-order function is the differential operator, which returnsthe derivative of a function.

Higher-order functions are closely related tofirst-class functions, in those higher-order functions and first-classfunctions both allow functions as arguments and results of other functions. Thedistinction between the two is subtle: "higher-order" describes amathematical concept of functions that operate on other functions, while"first-class" is a computer science term that describes programminglanguage entities that have no restriction on their use (thus first-classfunctions can appear anywhere in the program that other first-class entitieslike numbers can, including as arguments to other functions and as their returnvalues).

If above explanation is still too vague, let’s lookat the semantics and syntactic of Perl regarding to functional programming tohave a better understanding.

Functional programming syntacticin Perl

In Perl, ‘{‘ and ‘}’ are used to define a block,without ‘sub’ key word, {…} is used to define anonymous hash, but with a ‘sub’in front of {…} without naming it, it would be the definition of Perl anonymousfunction. You can assign this anonymous function to a scalar variable, it iscalled: function reference in Perl, if you put anonymous function into anotherfunction, then it becomes a closure.

See following example for function reference:

$func = sub {print “I am a function\n”}; # this defined an anonymous function reference.

$func->(); # output: I am afunction

 

Seefollowing example for closure:

 

1.       subouter {

my$x = shift;

my$inner = sub {

my$y = shift;

if($y == 0) { $x++ }

return$x + $y;

};

return$inner;

}

 

2.       $func= outer(10);

3.       print$func->(4);  # output is 14

4.       print$func->(0); # output is 11

5.       print$func->(4); # output is 15

 

Anonymousfunction is much easier to be understood, but closure might take us a littlemore time to see it through. From above closure example, an anonymous functionis defined in function: outer, and is returned as return value of function:outer, this is also the demonstration of the concept of: first-class function(function itself can be used a function input or output). In above example,line 3 has output 14 because it is a result of 10+4. But in line 4, since theinput of inner function is 0, it increase x (which is 10 by then) at first,then return 11+0=11; Finally, in line 5, because x now is 11, so the output is11+4=15. The magic here is that although x is defined as alocal variable in function: outer, but its function area is out of scope of ‘outer’because it is used by closure: inner, which makes it appear as a global variable regarding to closure: inner.

As we mentioned before, anonymous function and closuresupport in Perl gives programmers following capabilities:

  • using function as other function’s parameter to dispatch function logics;
  • using function as data to separate data and data handling logic;
  • using function as function return value to be able to add pre and post action to function;

Somereal examples:

Concept: using functionas other function’s parameter to dispatch function logics.

Motivation: In test automation, we usually face a situation that we need to dispatchhandling logic to different named functions based on user input argument. Themost common way to do it is using ‘if/else’ or ‘switch’ to dispatch based oninput value, but the ugly part of this ‘if/else’ or ‘switch’ solution is thatwhen a new logic is added, you must change the dispatch code; with functionprogramming you can solve this problem clearly like following:

Codingexample:

sub_queryUniqueIdentifiersForEntityOfStorageArray {

    my $self = shift;

   $self->{logObj}->info("I am: ".(caller(0))[3]);

}

 

sub_queryUniqueIdentifiersForEntityOfStorageProcessor {

    my $self = shift;

   $self->{logObj}->info("I am: ".(caller(0))[3]);

}

 

sub_queryUniqueIdentifiersForEntityOfStoragePort {

    my $self = shift;

   $self->{logObj}->info("I am: ".(caller(0))[3]);

}

 

sub_queryUniqueIdentifiersForEntityOfStorageCapability {

    my $self = shift;

   $self->{logObj}->info("I am: ".(caller(0))[3]);

}

 

sub queryUniqueIdentifiersForEntity {

    my $self = shift;

    my %params =validate(@_, {

                         type  => { type => SCALAR, optional => 0 },

                         }

                       );

    my $EntityType =$params{'type'};

   

    my $subFunctionName= "_queryUniqueIdentifiersForEntityOf".$EntityType;

   

    my $functionCall =$self->can($subFunctionName);

   

    $functionCall->($self);

}

 

Explanation: In above example, we have a common entrance function called:queryUniqueIdentifiersForEntity(), it accepts 4 entity types: Array, Processor,Port and Capability. Entity type is passed in as a parameter, and we canfirstly organize it to one of the function name we want to call, and then usethe Perl UNIVERSAL object method ‘can’ to turn this string into a validfunction call reference, then call it directly. No ‘if/else’ or ‘switch’grammar is used. And if you want to add a new function to handle a new type let’ssay ‘Disk’ in the future, no code need to be modified inside function: queryUniqueIdentifiersForEntity().This also aligns tothe famous design principle: open to extending, close to modification.

Concept: usingfunction as data to separate data and data handling logic (callback function).

Motivation: The thing we need to do mostly in daily test is comparing one dataset with another. Sometimes the 2 data sets are gotten from different resources.For example, when we were doing VASA testing in VNXe, we need to compare thedata gotten from VASA API with the data gotten from UEMCli (VNXe command linetool). These 2 sets of API return same thing with different name and format. Forexample, VASA call a LUN id: ‘alternateIdentifier’with value format: LUN6, while UEMCli call alun id: lun_id with value format: 6 (number only).  How could we still separate the data set and validationlogic without hard coding the format transferring into validation logic? Hereis the example:

Coding example:

queryStorageLuns => {

       cliValidation => [{

                    objType            => 'virtualdisk',

                    vasaResponsePrefix =>'//QueryStorageLunsResponse/return',

                    toValidate         => [{

                                   vasaResponseName          =>'displayName',

                                   vasaToCliFormatTranslator => sub {return $_[0];},

                                   cliResponseName           => 'name',

                                   },

                                  {vasaResponseName => 'alternateIdentifier',

                                   vasaToCliFormatTranslator =>

                                      sub{return substr($_[0], 3)},

                                   cliResponseName => 'lun_id',

                                   },

                                  {vasaResponseName => 'thinProvisioned',

                                   vasaToCliFormatTranslator =>

                                      sub { if($_[0] eq 'true') {return 1;} else {return 0;}},

                                   cliResponseName => 'thin',

                                   },

                    ]

                   },

 

Explanation: Above code exampleis the data structure we designed for VASA API: ‘queryStorageLuns’ responsevalidation, we used anonymous function in this data structure as hash value of key:‘vasaToCliFormatTranslator’ which will take the passed in argument and changeits format on the fly. In this way, we can focus our design on data structure,focus on which item we want to compare and what kind of format we want tochange it to. All the validation logic will only need to go through this wholedata structure and call those anonymous function without need to know theinside logic. This kind of technology has another famous name: callbackfunction.

 

Another example can fall into this concept is the list iterationsupport in Perl, you can see how following code deal with every element in anarray without using ‘foreach’:

map {

                       if($vasaResponseValue ne $_) {

                            die

                              "not match:$vasaResponseValue VS $staticResponseValue!\n";

                        }

                        else {print"mached: $vasaResponseValue\n"}

                    } @{$staticResponseValue};

 

The $staticResponseValue is an Array reference, we want to apply thesame logic to each of its element, so we used Perl building function: map toiterate through the array and apply the anonymous logic (after map key wordinside {}) to each item of this array. Clearly enough, the logic behind keyword map is actually an anonymous function definition.

 

Concept: usingfunction as function return value to be able to add pre and post action tofunction. [2]

Motivation: During automation, sometimes we want to find out how much time oneparticular function call is consuming, using functional programming, we canachieve this by passing a function name in and return an another function outwith necessary add-on applied.

Coding example:

use Time::HiRes'time';

my (%time, %calls);

 

sub profile {

my ($func, $name) = @_;

my $stub = sub {

my $start = time;

my $return =$func->(@_);

my $end = time;

my $elapsed = $end- $start;

$calls{$name} += 1;

$time{$name} +=$elapsed;

return $return;

};

return $stub;

}

 

Explanation: The‘Profile’ function shown here takes a function as its argument and constructs andreturns a stub, which may be called directly or installed in the symbol table inplace of the original. When the stub is invoked, it records the current time in$start. Normally the

Perl ‘time’function returns thecurrent time to the nearest second; the ‘Time::HighRes’ module replaces thetime function with one that has finer granularity, if possible. The stub callsthe real function and saves its return value. Then it computes the totalelapsed time and updates two hashes. One hash records, for each function, howmany calls have been made to that function; the stub simply increments thatcount. The other hash records the total elapsed time spent executing each function;the stub adds the elapsed time for the just-completed call to the total.

 

Summary

Comparing with OO programming paradigms, I think the functionalstyle leads to a lighter-weight solution that is easier to use, and that keepsthe parameter functions close to the places they are used instead of stuck offin a class file. Another interesting comparison between OOand functional programming is that OO focus more on data (methods are theoperations valid to data), but functional programming focus more on logic,which give programmer capability to separate data and handling logic moreeasily.

 

All in all, properly using functional programming paradigms in Perl canmake your test code more structural and looked more logical, but it is never areplacement to OO programming paradigms, if you can understand both and usethem all in test automation, you will be able to generate highly effective and re-usablecodes no doublt!

 

 

Reference

[1] WikiPage: Functional programming

[2]Book: Higher Order Perl


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值