精华内容
下载资源
问答
  • 实战Perl脚本测试

    千次阅读 2013-03-16 16:30:27
    这是一篇关于perl脚本测试的总结性文章,其中提到了很多实用的模块,如果文中介绍的不够详细,请到cpan上搜索该模块并查阅其文档。  1基本语法检查 Perl语言的哲学是“There is more than one way to do it”,很...

    实战Perl脚本测试(转)

    这是一篇关于perl脚本测试的总结性文章,其中提到了很多实用的模块,如果文中介绍的不够详细,请到cpan上搜索该模块并查阅其文档。 
    1基本语法检查
    Perl语言的哲学是“There is more than one way to do it”,很多讨厌Perl的人总是拿Perl的这个特性来攻击Perl,而喜欢Perl的人却又极力推崇它。这里不讨论这个特性是好是坏,但不可否认的是,Perl自由的语法,尤其是灵活特性所带来的诸多陷阱,再加上滥用这种灵活的coder,perl代码要做到“难看、难懂、难维护”,的确很容易。 
    1.1第一步
    拿到一份待测试的perl脚本,第一步,我们检查一下代码里有没有加上“use strict;”以及“use warnings;”。这里我大胆断言一下:如果一个100行以上的脚本从开始书写到完成,都没有加上这两句的话,这个脚本十有八九是有bug的。
        之所以我这么说,是因为strict和warnings实在是两个非常重要的模块。简单的说,strict模块帮助我们“避免犯错”,warnings模块帮助我们“发现错误”。
    1.1.1 use strict;
    strict模块一共有strict "vars"、 strict "refs"、 strict "subs"三部分,以strict "vars"为例,此功能要求脚本在使用变量时必须事先声明。
        对于$_、@_此类全局变量而言,它们已经由Perl语言作了预定义,而其它我们需要声明的非全局变量,即所谓的词法变量,我们都必须用my来做声明。这样的限制,规范了变量的使用,以免全局变量、局部变量难以区分,从而造成困惑。

    为了更好地解释my,给出如下三点附注:
    1、my变量也就是"词法变量",其作用域最大时为其所在的脚本文件范围(当my声明在任何一个{}块之外时),最小时为其所在的最内层的{}块。 
    2、全局变量除了$_此类预定义变量外,还包括用our或use vars来声明的全局变量。 
    3、local可以将全局变量临时地"局部化",但它不创建新的变量,因此你在声明一个新变量时,请用 my 或 our。 
    关于strict模块的具体应用,举个例子: 
    {  $tmp = 3; }
    此处省略500行代码
    $tmp ++;

    即使是两个不相关的变量,你也很可能用了同一个变量名$tmp进行声明,如果没使用 strict 模块,那么$tmp变量默认将为全局变量,前一个$tmp使用后的值,将污染后一个$tmp,尽管你本来把它们当作两个完全不同的临时变量。
        使用my可以规范变量作用范围,把很多潜在的错误消灭在代码最初。而strict模块的作用(严格的来说,是strict "vars"的作用),则是强迫你使用my。
    1.1.2 use warnings;
    看一个例子,warnings模块是怎么帮我们发现bug的: 
    $ cat a.txt   
    1 a b 2
    3 c d 4
    4 d f
    9 k f 1

    $ cat b.pl 
    open FI,"a.txt" or die "cant open a.txt";
    while(<FI>){
            @ret = split /\s*/,$_;
            print  $ret[0]+$ret[3],"\n";
    }

    $ perl b.pl 
    3
    7
    4
    10

    这段代码的本意是,从a.txt中按行取出第1、4个字段,打印出加和值。从代码中,可以看到我们默认a.txt每一行都是至少有4条记录的,但如果出现一个异常,导致a.txt中,有一行只有3列值,这种错误我们希望能够检查出来。
        然而,在不加warnings模块检查的情况下,虽然$ret[3]值为空,perl是不会报警的。
    我们使用warnings模块的命令行形式的参数-w来执行一遍b.pl : 
    $ perl -w b.pl 
    3
    7
    Use of uninitialized value in addition (+) at b.pl line 4, <FI> line 3.
    4
    10
    可以发现,perl解释器给我们打出了警告,并且指明了代码和数据文件出错的具体地址,从而帮助我们发现了bug 。 
    1.2 用-c参数检查语法
    Perl运行脚本,分成”编译阶段"和"运行阶段"两个过程,在"编译阶段",perl解释器会分析一遍语法,如果语法有问题,则直接报错退出,而不进行其它检查,更不会进入"运行阶段"。
        有时候我们想检查语法,却又不想语法检查通过后整个脚本被执行,这时候我们可以用perl的 -c 参数,譬如 perl -c test.pl ,这个参数能帮助我们检查完语法后就直接退出,不管检查是否通过。

    不过,它有如下两个缺点:
    1、 它只能检查语法,而无法通过-w等参数一并进行告警检查。 
    2、它会执行BEGIN块,并且会检查use方法加载的模块,但它不会执行INIT和END块,也不会执行require方法加载的模块(因为require方法加载模块是在"运行阶段")。 
    1.3用-T参数检查注入式漏洞
    我们都知道SQL注入,其实perl代码测试也需要注意注入式危险。先来看一个例子: 
    print "${foo()}";
    这句代码的目的是,将用户输入的一个指向标量变量的引用的返回值的函数打印出结果来,可假如foo()被替换成system('rm *'),等我们发现打印的结果不对时,危害已经造成了。 解决的办法是使用污染模式(taint-mode)选项(用-T参数): 
    $ cat d.pl 
    sub foo { system 'rm *' };
    print ${foo()};

    $ perl -T d.pl
    Insecure $ENV{PATH} while running with -T switch at d.pl line 1.
    1.4其它建议
    如果源头上产出的就是一份风格良好的代码,这样代码的质量和测试的效率就高多了。因此,我们在书写代码时,可以参考如下建议(作为要测试perl代码的QA,我们也可以把这些建议反馈给RD们):
    1.4.1制定命名规范
    譬如,声明引用变量时,变量名加“_ref”后缀;定义包时,以大写字母开头...等待。Perl允许你同一个变量名,同时声明为几种不同的变量类型,譬如 my $a; my @a; 。这种特性给我们带来的代码维护上的困惑远大于它的价值。此类问题我们都可以制定规范约束,具体规范的例子可以参考《Perl最佳实践》的相关章节。 
    1.4.2注释!注释!!
    其实最重要的还是注释,注释虽非多多益善,但函数的输入输出及用途说明、复杂逻辑的说明,这些还是很必要的。我在读perl代码时,加的基本都是中文注释--写起来容易,读起来也容易;我还喜欢把变量代入值,写一个示范语句出来,一个简单的例子比几句话更容易明白。 
    1.4.3代码格式规范
    有兴趣的可以去cpan上搜一下perltidy,或者参考《Perl最佳实践》的相关章节,这里不多介绍。 
    1.4.4使用别名
    对于 $@、$"此类全局变量,如果觉得可维护性很差,可以考虑使用其别名来增加可读性。
        譬如代码 undef $/; $cont = < FILE >; 的作用是通过修改$/,将文件句柄FILE的所有内容读入一个变量。一个可读性更强的方法,是将 $/ 用它的别名 $INPUT_RECORD_SEPARATOR 或 $RS 来替代(后者看上去很awk,不是吗?这是因为当初Larry Wall从awk中借鉴了很多语法。)。
        具体的别名列表,可以查阅 perldoc perlvar 文档。 
    1.4.5复杂数据结构解引用时,不妨分解一下
    先看一段代码: 
    %hash = (
    a => [ [1,2,3,4,5],
    [2,3,4,5,6] ],
    b => [ [3,4,5,6,7],
    [4,5,6,7,8] ]
    );

    print ${${$hash{b}}[1]}[4];
    为了获取这个"数组的数组的哈希"数据结构的值"8",我使用了 ${${$hash{b}}[1]}[4] 这个复杂的表达式,当然,这已经可读性不错了,毕竟我加了很多大括号,可如果其他人来看,不得不硬着头皮去翻译。当数据结构更复杂时,硬头皮程度指数级增长。
        我的建议是把这样一个复杂的解引用语句,转换成几个简单的解引用语句,譬如: 
    my $_arrayarray_ref = $hash{b};
    my $_array_ref = ${$arrayarray_ref}[2];
    print ${$_array_ref}[4];
    2测试方法
    2.1看
    2.1.1最简单最常用的方法:print函数
    最常用的往往是最简单的,print函数即是如此。在指定位置添加print函数,输出需要查看的变量内容,能帮助我们解决大部分的调试需求。 
    2.1.2我在哪?
    如果有大量的print函数调用,那么输出的结果很可能需要我们仔细辨别哪条信息对应哪条print调用。此时,print函数中添加__LINE__、__FILE__信息能帮我们解决这个问题,前者表示当前行号,后者表示当前所在脚本文件名。 
    2.1.3我是谁?
    用print函数,能查看某个变量的内容,但涉及到复杂的数据结构,print就不能满足需求了。
        以下给出两种更强大的方案。 
    2.1.3.1复杂数据结构 - Data::Dumper模块 
    简单如一个哈希数据结构,用print函数也没法方便地输出全部内容的。这个时候就应该使用Data::Dumper模块了,使用方法很简单: 
    { require Data::Dumper; print Dumper(%hash); }  
    再复杂的数据结构,Data::Dumper模块都能输出完整内容。

        注:为了方便调试代码的管理,以及尽量不影响原先代码,在遇到多行代码时,我会用{}来组成一个代码块,并且使用require而非use -- 以免可能将原本代码中编译阶段的缺少模块的bug给掩盖掉。下同。 
    2.1.3.2深入了解变量属性 - Devel::Peek 
    Devel::Peek能帮助我们深入了解变量的细节属性,举个例子,我们也许对Perl的上下文环境的处理方法很是困惑,下面这个例子就是利用Devel::Peek帮助我们了解一个"字符串变量"是如何和一个"整数变量"相加的。 
    $ cat f.pl
    use Devel::Peek;

    my $a = 5;
    my $b = '5';

    Dump($a);
    Dump($b);
    print "\n === equal ===\n\n" if $a == $b;
    Dump($a);
    Dump($b);

    $ perl f.pl 
    SV = IV(0x67c368) at 0x67c370
    REFCNT = 1
    FLAGS = (PADMY,IOK,pIOK)
    IV = 5
    SV = PV(0x65dbc8) at 0x67c430
    REFCNT = 1
    FLAGS = (PADMY,POK,pPOK)
    PV = 0x676670 "5"\0
    CUR = 1
    LEN = 16

    === equal ===

    SV = IV(0x67c368) at 0x67c370
    REFCNT = 1
    FLAGS = (PADMY,IOK,pIOK)
    IV = 5
    SV = PVIV(0x6717f0) at 0x67c430
    REFCNT = 1
    FLAGS = (PADMY,IOK,POK,pIOK,pPOK)
    IV = 5
    PV = 0x676670 "5"\0
    CUR = 1
    LEN = 16
    其中,SV表示标量,IV表示整型,PV表示字符串型,IOK标志表明对象含有一个可用的整型值,POK标志表明对象含有一个可用的字符串值。
        从例子中,可以看到,$a和$b在刚声明的时候,分别是整型标量和字符串标量。在需要做数值比较时,作为字符串标量的$b将主动地从字符串型转换成整型,从而”既是字符串型,又是整型“。同理,如果你用 $a eq $b 进行比较,则整型变量$a将获取字符串型的属性。
    一般情况下,我们不需要了解这么多。如果说我们的工作是测试一个鸡蛋是否变质了,那我们闻和吃一般就够了,而没必要在显微镜下观察鸡蛋的蛋白质等结构。
        Devel::Peek就是我们的显微镜。如果的确有兴趣用这台显微镜研究蛋白质结构,请参考 perldoc perlguts 以及 perldoc Devel::Peek 文档。 
    2.1.4我怎么想? - B::Deparse
    有时候,对于某些晦涩的语句,我很想知道perl是怎么理解它的。譬如我想知道 while( < FILE > ) 的终止条件是< FILE >返回空值呢,还是undef,这时候可以使用如下命令: 
    $  perl -MO=Deparse -e "while( <FILE> ) {}"
    while (defined($_ = <FILE>)) {
        ();
    }
    -e syntax OK
    由此看出,终止条件是<FILE>返回undef。
    具体的内容,可以查看B::Deparse文档。 
    2.1.5用Carp模块获取详细的警告和错误信息
    这里推荐Carp模块的carp和croak两种方法,作为warn和die的替代。推荐的理由在于,carp和croak在具有warn和die的功能外,还能输出更多的信息,以方便我们调试。 举例: 
    $ cat b.pl 
    use Carp;

    sub foo {
        unless(defined $_[0]) {
            warn "warn message\n";
            print " \n";
            carp "carp message\n";
        }
    }

    foo();

    $ perl b.pl 
    warn message

    carp message
    at b.pl line 6
    main::foo() called at b.pl line 10
    如果还需要获取堆栈信息,可以进一步用cluck替代carp。相对地,die - croak - confess 也构成一组调试方法。 
    2.1.6比较
    可能在某一处,我们希望做一些复杂变量的比较工作。这里我们可以使用一些Test::Builder类的模块,譬如: 
    { require Test::Deep; require Test;   print Test::Deep(\%a,\%b,"cmp ok")  }
    常用变量比较方法 

    2.1.7在运行阶段随时进行查看
    有时候,程序运行时间很长,而我们希望在运行过程中,随心所欲地观察一些信息。这种情况下,我们可以自定义信号处理函数,譬如: 
    $SIG{INT} = sub {require Data::Dumper;  print Dump(%hash)};
    INT信号默认行为是终止程序,我们可以把它改成输出某个我们想了解的值。如此,你可以在每次需要查看处理进度信息时,按ctrl+c就行了。 
    2.1.8既是注释,又是debug语句
    有一个模块,它能让你添加一些语句,使得这些语句在平常情况下是一些注释,在你需要的时候就是调试信息输出语句。这个模块就是Smart::Comments,这里不多介绍,具体可以查看其文档。 
    2.2改
    2.2.1用POD快速注释掉一段代码
    我们知道用 # 符号可以注释掉一行代码,但这样效率太低了,对于整块的代码,我们可以利用POD来快速注释,譬如: 
    =head
    此处省略100行需要被注释的代码
    =cut  
    注意 “=head”和“=cut”都需要顶格写。关于POD的详细资料,可以查看 perldoc pod 文档。
        此外,你还可以用"__END__"或"__DATA__"将所在行之后的内容全部“注释”掉。 
    2.2.2修改代码
    直接修改代码,假定某些输入条件,测试后续逻辑的正确性,这是我们常用的方法。
    这里需要特别提到的是关于正则表达式,如果我们发现正则表达式语句比较复杂,建议你用几条简单的正则表达式去替代被测的复杂正则表达式语句,并且在同样的 输入条件下,验证输出结果是否一致。这种方法尤其适用于输入是大数据量的情况下,通过大量的实验,我们很可能会发现一些在新旧正则表达式下输出不一致的 case。 
    2.2.3重载库函数
    对于当前脚本中的函数,我们可以直接修改函数实现的代码。可某些情况下,我们想要修改当前脚本所调用的库文件里面的函数。这种情况下,直接修改库文件比较麻烦,尤其是标准的库文件,我们想修改它还需要root权限。一个比较简单的方法是重新实现该函数,从而覆盖掉库文件中的同名函数。
        举个例子,我们知道Time::Local模块的timelocal函数是localtime的倒置,假设我需要修改timelocal函数,使得获取到的值都是输入参数的日期的下一年的反转结果。
    具体方法如下: 
    $ cat g.pl 
    use Time::Local;
    use subs 'timelocal';         # 告诉解释器,timelocal函数需要重载

    *Time::Local::timelocal = sub {        # 用typeglob的方法,将timelocal指向新的函数定义
        my @tmp = @_;
        $tmp[5] += 1;
        return timelocal(@tmp);    # 调用原先的timelocal函数
    };

    my $t = time();
    print $t,"\n";

    my @a = localtime($t);
    print Time::Local::timelocal(@a),"\n";

    $ perl g.pl 
    1291188534
    1322724534
    从例子中看到,重载后的timelocal函数,使得输出的值比初始值大了一年。
    2.3跟踪和调试
    2.3.1用Devel::Trace跟踪
    大家都知道shell中有-x参数,可以设定了跟踪每一行代码的执行结果,在perl中,有一个模块能起到相似的作用,那就是Devel::Trace。
    使用方法很简单,perl -d:Trace test.pl,具体可以CPAN上查看文档。 
    2.3.2用debug调试
    ? 基本的debug命令 
    Debugging task 
    Debugger command

    To run a program under the debugger  > perl -d program.pl 
    To set a breakpoint at the current line  DB<1> b 
    To set a breakpoint at line 42  DB<1> b 42 
    To continue executing until the next break-point is reached  DB<1> c 
    To continue executing until line 86  DB<1> c 86 
    To continue executing until subroutine foo is called  DB<1>c foo 
    To execute the next statement  DB<1> n 
    To step into any subroutine call that's part of the next statement  DB<1> s 
    To run until the current subroutine returns  DB<1> r 
    To print the contents of a variable  DB<1> p $variable 
    To examine the contents of a variable  DB<1> x $variable 
    To have the debugger watch a variable or expression, and inform you whenever it changes  DB<1>w$variable 
     DB<1> wexpr($ess)*$ion 
    To view where you are in the source code  DB<1> v 
    To view line 99 of the source code  DB<1> v 99 
    To get helpful hints on the many other features of the debugger  DB<1> h 
    上面的debug的基本命令,已经能应付绝大部分情况下的调试需求,除此外,这里还想特别介绍一下 a 命令,通过 a LINE COMMAND 的方法,我们可以设置一个在执行第 LINE 行程序之前的动作,而我们可以利用这个COMMAND,设定一些复杂的操作,以更好地获取调试信息。
    举个例子:
    $cat k.pl     
    %ha = ( a => [1,2,3],
    b => [4,5,6] );
    %hb = ( a => [1,2,3],
    b => [4,5,7] );
    print "1\n";

    $ perl -d k.pl    

    Loading DB routines from perl5db.pl version 1.33
    Editor support available.

    Enter h or `h h' for help, or `man perldebug' for more help.

    main::(k.pl:1): %ha = ( a => [1,2,3], 
    main::(k.pl:2):         b => [4,5,6] );
    DB<1> a 5 use Test::More tests => 1; use Test::Differences; print eq_or_diff ${$ha{b}}[2],${$hb{b}}[2],"e or d";
    DB<2> n
    main::(k.pl:3): %hb = ( a => [1,2,3], 
    main::(k.pl:4):         b => [4,5,7] );
    DB<2> n
    main::(k.pl:5): print "1\n";
    1..1
    not ok 1 - e or d
    #   Failed test 'e or d'
    #   at (eval 24)[/opt/ActivePerl-5.12/lib/perl5db.pl:638] line 1.
    # +---+-----+----------+
    # | Ln|Got  |Expected  |
    # +---+-----+----------+
    # *  1|6    |7         *
    # +---+-----+----------+
    0  
    更多的内容,请参考 perldoc perldebug 文档。 
    3测试对象
    3.1测试.pl脚本文件
    这里的.pl脚本文件,指的是被直接运行的脚本。这样的脚本,一般的特点是包含了很多库文件,并且代码比较零散--不像库文件那样绝大部分代码都是一个个封装好的函数。
        测试这样的脚本时,除了前面讲到的那些测试方法外,这里再介绍一个测试框架:Test::More。
        Test::More是perl脚本的测试框架模块,除了下表整理的一些常用方法,它还有SKIP、TODO等测试内容标记,更强大的地方 是,Test::More由于基于Test::Builder模块,它能配合其它Test::Builder类的测试模块一起工作。
    具体参考 perldoc Test::More; perldoc Test::Builder; 
    ? 常用Test::Builder类模块及其方法 
    模块名 
    模块简介 
    方法名 
    方法简介

    Test::More 
    强大的测试框架  ok  判断真假 
      is/isnt  字符串比较,类似eq/ne 
      like/unlike  正则比较,匹配/不匹配 
      cmp_ok  可以指定操作符地比较 
      can_ok  被测模块是否导出函数到当前命名空间 
      isa_ok  对象是否被定义或对象的实例变量确实是已定义的引用 
      subtest  生成测试子集 
      pass/fail  直接给出通过/不通过 
      use_ok  测试加载模块并导入相应符号是否成功 
      require_ok  类似use_ok 
      is_deeply  复杂数据结构的比较,加强版的is 
    3.2测试.pm库文件
    如前面所说,库文件一般都是由一个个封装好的函数组成,这样的perl脚本,最适合做单元测试了。单元测试推荐Test::Class模块,具体的内容参考另外一篇文章《perl单元测试》。 
    3.3性能测试
    这里的性能测试主要指的是脚本运行时间的检测,具体依然参考《perl单元测试》。 
    3.4覆盖率测试
    覆盖率测试,具体内容参考《perl单元测试》。。 
    4 Perl陷阱和缺陷
    关于陷阱和缺陷,perl有专门的文档,具体参考 perldoc perltrap 。以下列举的内容是对它的一个补充,另外也是实际工作中更易犯的一些错误。 
    4.1基本语法
    4.1.1真与假
    除了""和"0",所有字符串为真
    除了 0,所有数字为真 
    所有引用为真 
    所有未定义的值为假
    空列表"()"、"undef"均为假
    4.1.2数组和哈希的初始化,不要用undef
    $ cat b.pl 
    @a = undef;
    @b = ();
    %c = undef;
    print scalar @a,"\n";
    print scalar @b,"\n";
    print scalar keys %c,"\n";

    perl b.pl
    1
    0
    1
    从例子可以看出,用undef作为右值,实际上是给数组或哈希初始化了一个值,该值为undef,而非预想的初始为空。 
    4.1.3对 $_ 的值,及时存取
    $_ 是每个perler初学时最早碰到的让人困惑的全局变量,它是缺省的输入和模式搜索空间,很多操作和函数中都会将输出缺省地赋给它。因此,养成及时存取 $_ 变量的习惯,能够避免很多陷阱。 
    while(<FI>){
        $tmp_a = $_;
        foreach(@arr){
            $tmp_b = $_;
            ......
        }
    }
    4.1.4对全局变量的修改,注意及时恢复
    把一个文件句柄对应的文件内容,赋值给一个scalar变量,常用 undef $/; $cont = < FILE_A > 。但这样将会修改全局变量 $/ ,如果没有改回去的话,后续的while(< FILE_B >)之类所有对文件句柄的钻石符操作,均将一次读入整个文件。
    修改的办法,是将变量赋回原先的值: 
    undef $/;
    $cont = <FILE_A>;
    $/ = "\n";   # 重置为初始值
    while(<FILE_B>) 
    更好的办法,是用 local 函数,将修改局限在最小的闭合块中: 
    {
        local $/ = undef;
        $cont = <FILE_A>;
    }
    while(<FILE_B>) 
    4.1.5用foreach、for的两个注意点
    先来看一段测试代码 
    $ cat e.pl 
    $i = 0;
    @a = (1,2,3);
    foreach $i (@a) {
        $i = -$i;
    }
    print "$i\n";
    print "@a\n";

    $ perl e.pl 
    0
    -1 -2 -3
    从中我们可以看到两点:
    1、 变量 $i 尽管中foreach循环(或for循环)中最后的一次值是3,但退出循环后,其值还是保持进入循环时的值。也就是说,$i 变量在foreach循环中,被local了。
    2、 $i 在foreach循环过程中,是@a数组当前值的别名,因此修改$i值,也就是直接修改了@a数组。在for、grep函数中,同样存在这个问题。 
    4.1.6子例程传参时不会拷贝参数,注意不要修改原值
    先来看如下一个例子: 
    $ cat h.pl 
    #use Devel::Peek;

    my @a = qw(1 2 3);
    #Dump(\@a);

    sub bezero {
        #   Dump(\@_);
        $_[1] = 0;
        print "===\n";
    }

    print "@a\n";
    bezero(@a);
    print "@a\n";

    $ perl h.pl 
    1 2 3
    ===
    1 0 3
    我们看到,数组 @a 在被当作参数传入函数bezero后,被修改了值。由此,我们可以怀疑,在传参时,@_其实是@a的别名,而非拷贝。为了证实这个猜测,我们可以利用前面 提到的Devel::Peek模块观察这两个数组的地址是否一致。因此我们打开脚本中的注释,然后重新运行脚本,观察结果如下: 
    $ perl h.pl 
    SV = IV(0x67bc30) at 0x67bc38
    ... ....
    SV = IV(0x67bc30) at 0x67bc38
    ... ...
    两个数组的地址完全一致。
    通过这个例子,我们的结论是:函数传参时,如果你不想修改参数值,记得把参数在函数内部拷贝一份后再使用。

    4.1.7用each多次遍历同一hash时,注意及时归位迭代游标
    $ cat a.pl
    %h = ( a => 1, b => 2 );
    while(($a,$b)=each %h){
        print "$a $b\n";
        print "======\n";
        last;
    }
    while(($a,$b)=each %h){
        print "$a $b\n";
    }

    $ perl a.pl 
    a 1
    ======
    b 2
    这个例子中,有两处使用了each函数,由于前面一个while在获取一次值后,就退出循环,但没有重置迭代游标,导致第二个while从上一次迭代的位置开始。
        如果你期望第二次while中也能用each完整地遍历哈希,你需要重置迭代游标。重置的方法是完整地读取一遍该哈希,譬如用keys函数、values函数、把该哈希当作右值赋值给数组或另一个哈希。当然,你用 while(each %h){} 什么也不做地遍历一遍,也能重置迭代游标,只是这样的代码实在有点丑陋。
        而我的做法是, 从来不用each函数,而是用keys函数获取key后再取value ,我的哈希遍历方法如下: 
    foreach my $key (keys %h) {
        print "$key $h{$key}\n";
    }
    虽然多敲了些代码,但这样做我不用担心前面的陷阱,因为keys函数一次就遍历完了哈希。
        你可以 perldoc -f each; perldoc -f keys; perldoc -f values; 了解更多。
    ? Perl内部是怎么维持这个迭代游标的? 
    1. 在Perl内部,散列称为 HV (hash value),并使用 HE 结构体表示键/值对,使用 HEK 结构体表示所有关键字。
    2. Perl用RITER, EITER两个字段来实现访问散列所有元素的单向迭代器。RITER 是作为数组下标的整数,而 EITER 是一个指向 HE 的指针。迭代器是从 RITER = -1 和 EITER = NULL 开始的,完成一次迭代后,RITER和EITER的值将会变化。其中RITER在首次迭代后,值变成1,之后每次成功迭代,值都将累加1,直到迭代完成 后,复位为-1。
    知道了这些细节,我们就可以用 Devel::Peek 模块的 Dump 函数更方便地观察迭代过程了,感兴趣的话,你不妨一试。 
    4.1.8调用函数时,用foo(),而不要用foo或&foo
    以如下一段代码为例: 
    $ cat a.pl
    use strict;
    use warnings;
    sub double {
        return 0 if scalar @_ == 0;
        return 2 * $_[0];
    }
    print double - 3;        

    $ perl a.pl
    -6
    这里原意是要打印出 double函数的返回值减去3以后的值,也就是应该打印结果为 -3 ,可结果却是 -6 。
        我们可以利用前面介绍的 B::Deparse 模块,看一下perl是怎么理解代码的: 
    $ perl -MO=Deparse a.pl
    sub double {
        use warnings;
        use strict 'refs';
        return 0 if scalar @_ == 0;
        return 2 * $_[0];
    }
    use warnings;
    use strict 'refs';
    print double(-3);
    a.pl syntax OK
    很清楚地发现,perl把 double -3 理解成了 double(-3)。为了避免这种问题,在函数调用的时候,我们必须明确地写成 double() - 3 ,而如果你就是想表达 double(-3) 的话,也要明确地写成 double(-3) ,以免代码让人理解错。

        另外需要注意,不要写成 &double(), 这种写法是perl 4版本的语法,虽然目前perl 5兼容了这种写法,但它会带来一些困惑,譬如:
        $curr_pos = tell &get_mask( );     # means: tell(get_mask( )) 
        $curr_time = time &get_mask( );   # means: time() & get_mask( )

    当然,在作为函数引用的时候,还是需要用的,譬如:
    $SIG{PIPE} = \&Plumber;    # 定义PIPE信号的处理函数
    4.2正则匹配
    4.2.1变量内插时,注意特殊字符的转义
    可以使用quotemeta函数,也可以使用\Q..\E,但总之,在需要变量内插从而构成正则表达式时,注意特殊字符的转义。 
    4.2.2在用$1获取匹配结果时,确保匹配是成功的
    先来看如下一个例子: 
    $ cat a.pl 
    $str = 'a2';
    $str =~ /(\d)/;
    print $1,"\n";
    $str =~ /(3)/;
    print $1,"\n";

    $ perl a.pl 
    2
    2
    $1是全局变量,匹配失败时并不会修改它的值,也不会重置为undef,因此,每次使用它时,请确保加上匹配成功的判断,譬如 if( $str =~ /(\d)/ ){ print $1; } 。 
    4.2.3  /g -- 控制迭代匹配
    /g 是全局查找所有匹配的修饰词。在列表环境下,它能一次性返回所有匹配结果,在标量环境下,它每次返回一个匹配结果。标量环境下进行迭代匹配时,需要了解匹配偏移量的知识,否则很容易出错。

    我的建议是:
    1、列表环境下使用/g,大胆用吧,不用担心。
    2、标量环境下使用/g,如果需要对同一个字符串进行多次/g匹配,注意是否需要重置匹配偏移量。
    ? 与迭代匹配相关的 
    迭代匹配修饰符: /g、/cg 
    上次匹配位置断言: \G 
    上次匹配位置函数: pos($str)  
    ? 不同上下文环境下的/g 
     列表环境  标量环境 
    显式  @res = m/(\d)/g;  $res = m/(\d)/g; 
    隐式  print m/(\d)/g;  m/(\d)/g; 
      while( m/(\d)/g ) { } 
    功能  列表环境下,/g一次性返回所有匹配结果列表  标量环境下,/g迭代匹配,每次返回一个匹配结果

    ? 用/g、/cg和pos来控制迭代 
    匹配类型 
    匹配尝试开始位置 
    匹配成功时的pos值 
    匹配失败时的pos设定

    m/…/  字符串起始位置(忽略pos)  重置为undef  重置为undef 
    m/…/g  字符串的pos位置  匹配结束位置的偏移值  重置为undef 
    m/…/gc  字符串的pos位置  匹配结束位置的偏移值  不变 
    4.2.4正则表达式很强大,但不要滥用
    首先来做一个练习:给你5分钟,让你写出一个判断"年月日"字符串是否合法的正则表达式。
        sleep 5 * 60; 
        好了,坦白说,你是不是不到两分钟就放弃了?我相信这样的正则表达式肯定可以写出来,因为perl的正则表达式非常强大,它支持"环视"(look around),支持在表达式中嵌入条件判断、代码,你可以把一个正则表达式写成一个由复杂的逻辑判断语句构成的式子。不过,既然如此,何必还要硬着头皮写正则表达式呢?我们干脆用一些 if...else... 语句去判断好了。
        况且,即使你花了N分钟写出来了,这样的表达式,可读性也是非常差的。
        因此,给一个建议:逻辑复杂的时候,不如用几条语句去代替一个正则表达式。

    除此外:
    1、对于定长的记录,pack/unpack是更好的选择。
    2、分析html、xml等页面时,尤其是很难找到一个固定的字符串去锚定匹配结果时,推荐使用HTML::Parser、HTML::TreeBuilder、XML::Simple之类的模块。 
    4.3与Shell共舞
    perl脚本调用shell命令,shell脚本中执行perl命令行语句,这是经常会碰到的场景。作为胶水语言,perl能与shell很好地共舞,但前提是避免落入如下陷阱。
        perl脚本调用shell命令,一般有三种方式:``或qx//、system()函数、exec函数。关于这几种方式的含义和使用方法,可以查看相关文档。这里的内容都是使用中的陷阱。

        注:这里写”调用shell命令"并不严谨,我的确切意思是,"向shell提交对外部命令调用的请求"。 
    4.3.1区分perl中的$?跟shell中的$?
    在shell中,我们知道 $? 表示上一条命令执行后的返回状态,而在perl中, $? 表示上一次管道关闭,反勾号(``)命令或者 wait,waitpid,或者 system 函数返回的状态。 它的另一个名字是$CHILD_ERROR。它是一个16位的状态值,高8位是子进程的退出值,在低位,$? & 127 告诉你该进程是因为哪个信号(如果有)退出的,而 $? & 128 汇报该进程的死亡是否产生一个内核的倾倒。

    说具体一点,我们需要注意以下两点: 
    ? 不要用 $? 去判断前一条语句是否执行成功 
    open FI,"a.txt"; die "open a.txt failed" if ($? = 0); 这种写法是错误的,正确的写法是 open FI,"a.txt" or die "open a.txt failed"; 。原因很简单,在perl中,$? 并不是“上一条命令”执行后的返回状态。

    ? 子进程返回失败时,$? 的值是256,而非1 
    举例: 
    $ cat p.pl     
    system("ls file_not_exist") ;
    print $?;

    $ perl p.pl
    ls: file_not_exist: No such file or directory
    256
    原因前面讲了,$?的高8位存的是子进程的退出值,因此子进程退出值是1时,$?的值就是256。 
    4.3.2调用外部命令并获取返回值后,注意chomp
    譬如 chomp($host=`hostname -i`) ,如果少了chomp,但$host变量获取到的将是末尾带有\n的ip值,为后续处理埋下了隐患。 
    4.3.3不要调用shell的内建命令
    shell的内建命令,实际上都是shell的内部函数,这些内建命令,你用 which 是查不到路径的。内建命令包括 cd、export、umask、source 等等,具体的你可以 man builtins 查看。
    对于内建命令,譬如 cd,你直接用 system("cd") 执行会出错,错误是无法找到该文件或目录(你可以用代码 system("cd"); print $!; 试一下),在命令前加 sh -c 是个解决方案。
    不过调用shell的内建命令,效果可不是你预想的那般,测试代码如下: 
    system("sh -c cd ./dir && touch cba");
    system("touch abc");
    这段代码试图在 ./dir 目录下,建两个文件,运行后却发现,两个文件都被建在了当前目录,而非 ./dir 目录。
        原因在于perl每次在用system()调用外部命令时,都是先做一个fork,而外部命令的执行是在fork的子进程中进行的,它做的 cd 操作,无法更改父进程(也就是perl脚本进程)的工作路径。
        解决的办法是用perl自己的函数,譬如用 chdir 替换 system("sh -c cd")、用 umask 替换 system("sh -c umask") 等等。 
    4.3.4调用外部命令时,注意特殊字符的转义
    简单不严谨地说,perl对'('、'<'之类的特殊字符是不需要转义的,而shell需要,因此在有可能涉及此类特殊字符时,需要特别留意。
        以下是一段测试代码: 
    $a = 'abc(123)sdf';
    $b = quotemeta $a;
    qx/echo $a/;
    `echo $a`;
    system("echo $a");
    exec("echo $a");
    试着执行一下,四条语句均会报错。把$a改成$b,四条语句都改为正确。

    4.3.4慎用Shell.pm模块
    Shell.pm是perl的标准模块,它能够让你在perl脚本中直接运行shell命令,但由于它内部实现时对quoting的处理有缺陷,导致在对包含'('、'<'之类字符的参数处理时依然会有错误。
    测试代码如下:
    $ cat d.pl 
    use Shell;
    $a = 'abc(123)sdf';
    $b = quotemeta $a;

    echo($a);
    print "=============\n";
    echo($b);

    $ perl d.pl
    sh: -c: line 0: syntax error near unexpected token `('
    sh: -c: line 0: `echo abc(123)sdf'
    =============
    sh: -c: line 0: syntax error near unexpected token `('
    sh: -c: line 0: `echo abc\\(123\\)sdf'
    可以看到,Shell模块的转义问题更加使人困惑,不管有没有quotemeta,都会报转义相关的错误。
    查一下perldoc Shell文档,可以看到:
    BUGS
           Quoting should be off by default.
    文档告诉我们,转义的问题其实一直都在。
    如果你感兴趣,还可以debug到Shell.pm模块内部,看看该模块对转义是怎么处理的,从而了解问题究竟出在哪里。这里先公布一下我的结论:以版本号为0.72_01的Shell.pm为例,问题出在第109~110行(s/(['\\])/\\$1/g;$_ = $_;),这里对转义的处理过于简单了。解决方法是将第110行代码改成$_ = quotemeta $_; 我的实验结果是它解决了转义问题,但我并不确定它是否会带来其它的bug,聪明的你可以深究一下。
    4.3.5 shell调用perl语句,注意变量内插
    大家在写shell脚本时,经常调用awk、sed命令进行一些文本相关的处理,其实perl也有 one-line 模式,也能被shell调用,并且功能远比awk、sed强大。具体的命令行模式的perl如何使用,请参考 perldoc perlrun 文档。
        这里讲一个需要注意的问题(其实类似例子更应该算是shell的”陷阱“,并且在shell脚本调用awk时也同样需要注意): 
    $ cat log
    key:1us time:11us
    key:2us time:12us else:null
    key:3us type:arr time:13us

    $ cat a.sh
    pattern='time'
    cat $1 | perl -ne '{$sum += $1 if /$pattern:(\d+)us/;} END{print $sum;}'

    $ sh a.sh log
    6
    脚本a.sh的设计功能是从命令行参数中读入一个日志文件,利用perl语句匹配出time值,然后计算出time值的加和。根据设计,运行结果应该是36,而非6。
        问题出在对$pattern的传值上,注意到脚本里是用''单引号来引用perl执行语句的,我们知道单引号在shell里是"hard quote",凡在hard quote中的所有meta都会被关闭特殊含义,包括这里的'$'符号。因此这里无法将$pattern的值传入perl语句,导致$pattern的值 为空,从而错误地匹配了 ':(\d+)us' ,并且捕获了它匹配到的第一个值,也就是"key"后面的数字。
        如果把单引号改成双引号,能解决$pattern变量的传值问题,但却导致$sum和$1这两个原本perl语句中的变量被shell进行了变量内插,这样的错误依然不能接受。
        正确的解决办法是将perl语句的那一段,分情况使用单引号和双引号,更改后的语句和运行结果如下: 
    $ cat b.sh
    pattern='time'
    cat $1 | perl -ne '{$sum += $1 if /'"$pattern"':(\d+)us/;} END{print $sum;}'

    sh b.sh log
    36

     

    (全文完

    展开全文
  • 软件测试文档

    千次阅读 2019-09-17 16:14:31
    什么是软件测试 通过手工和自动化工具对被测对象进行检测,验证实际结果和预期结果之间的差异。 软件测试的原则 ...7 妥善保存一切测试文档 软件测试的目的 1 暴露软件中的缺陷和BUG 2 记录软件运行中产...

    什么是软件测试
    通过手工和自动化工具对被测对象进行检测,验证实际结果和预期结果之间的差异。
    软件测试的原则
    1 测试是为了证明软件存在缺陷
    2 测试应该尽早介入
    3 注意测试缺陷的群集效应80-20
    4 杀虫剂现象
    5 合法数据和不合法数据和边界值,网络异常和电源断电等
    6 回归测试防止出现更多问题
    7 妥善保存一切测试文档

    软件测试的目的
    1 暴露软件中的缺陷和BUG
    2 记录软件运行中产生的一些数据,为开发提供改良的数据支持

    为什么需要软件测试
    1 功能实现且正确执行
    2 软件运行的信息数据
    如果一个产品开发完成之后发现了很多问题,说明此软件开发过程很可能是有缺陷的,因此,软件测试的目的是保证整个软件开发过程是高质量的。

    测试分类
    1 单元测试 分单元
    2 集成测试 多个单元
    3 系统测试 用户角度-功能主体
    4 验证测试 α测试-内测 β测试-公测 UAT测试-客户验收使用
    系统测试分类
    1 功能测试
    2 性能测试
    3 安全测试
    4 兼容性测试

    测试方法
    1 按照测试对象分类
    白盒测试
    黑盒测试
    灰盒测试
    2 按照测试对象是否执行分类
    静态测试
    动态测试
    3 按照测试手段进行分类
    手工测试 灵活改变测试操作和环境
    自动化测试 1 自己写脚本 2 第三方工具进行测试

    软件质量
    1 维护性
    2 移植性
    3 效率性
    4 可靠性
    5 易用性
    6 功能性

    软件测试流程
    1 需求分析
    2 设计用例
    3 评审用例
    4 配置环境 操作系统+ 服务器+数据库 +软件依赖
    5 执行用例
    6 回归测试及缺陷跟踪
    7 输出测试报告
    8 测试结束

    软件架构
    BS browser浏览器 + server服务器
    CS client客户端 + server服务器
    1 标准上 BS是在服务器和浏览器都存在的基础上开发
    2 效率 BS中负担在服务器上 CS中的客户端会分担,CS效率更高
    3 安全 BS数据依靠http协议进行明文输出 不安全
    4 升级上 bs更简便
    5 开发成本 bs更简单 cs需要客户端 安卓和ios

    软件开发模型
    瀑布模型
    1 需求分析
    2 功能设计
    3 编写代码
    4 功能实现 切入点

    5 软件测试 需求变更
    6 完成
    7 上线维护

    是一种线性模型的一种,是其他开发模型的基础
    测试的切入点 要留下足够的时间 可能导致测试不充分,上线后才暴露

    优点
    开发的各个阶段比较清晰
    需求调查 适合需求稳定的产品开发
    当前一阶段完成后,您只需要去关注后续阶段
    可在迭代模型中应用瀑布模型
    可以节省大量的时间和金钱
    缺点
    1)各个阶段的划分完全固定,阶段之间产生大量的文档,极大地增加了工作量。
    2)由于开发模型是线性的,用户只有等到整个过程的末期才能见到开发成果,从而增加了开发风险。
    3)通过过多的强制完成日期和里程碑来跟踪各个项目阶段。
    4)瀑布模型的突出缺点是不适应用户需求的变化
    瀑布模型强调文档的作用,并要求每个阶段都要仔细验证。
    快速原型模型
    部分需求 - 原型- 补充 - 运行 外包公司
    预先不能明确定义需求的软件系统的开发,更好的满足用户需求并减少由于软件需求不明确带来的项目开发风险。
    不适合大型系统的开发,前提要有一个展示性的产品原型,在一定程度上的补充,限制开发人员的创新。

    螺旋模型
    每次功能都要先进行风险评估,需求设计-测试
    很大程度上是一种风险驱动的方法体系,在每个阶段循环前,都进行风险评估。
    需要有相当丰富的风险评估经验和专门知识,在风险较大的项目开发中,很有必要,多次迭代,增加成本。

    软件测试模型
    在这里插入图片描述
    需求分析-概要设计-详细设计-开发-单元测试-集成测试-系统测试-验收测试
    优点
    清楚标识软件开发的阶段 包含底层测试 和高层测试
    采用自顶向下 逐步求精的方式把整个开发过程分成不同的阶段,每个阶段的工作都很明确,便于控制开发过程。

    缺点
    程序已经完成,错误在测试阶段发现或没有发现,不能及时修改
    而且需求经常变化 导致V步骤反复执行,工作量很大。

    W模型
    开发一个V 测试一个V
    在这里插入图片描述
    用户需求 验收测试设计
    需求分析 系统测试设计
    概要设计 集成测试设计
    详细设计 单元测试设计
    编码 单元测试
    集成 集成测试
    运行 系统测试
    交付 验收测试

    优点
    测试更早的介入,可以发现开发初期的缺陷,降低成本
    对每个阶段都进行测试,包括文档,便于控制项目过程

    缺点
    依赖文档,没有文档的项目无法使用,复杂度很高,实践需要很强的管理

    H模型把测试活动完全独立出来,将测试准备和测试执行体现出来
    测试准备 - 测试执行
    就绪点
    其他流程 ----------设计等
    v模型适用于中小企业 需求在开始必须明确,不适用变更需求
    w模型适用于中大企业 包括文档也需要测试(需求分析文档 概要设计文档 详细设计文档 代码文档)测试和开发同步进行H模型对公司参与人员技能和沟通要求高

    测试阶段
    单元测试-集成测试-系统测试-验证测试
    是否覆盖代码
    白盒测试-黑盒测试-灰盒测试
    是否运行
    静态测试-动态测试
    测试手段
    人工测试-自动化测试
    其他测试
    回归测试-冒烟测试

    功能测试
    一般功能测试-界面测试-易用性测试-安装测试-兼容性测试
    性能测试
    稳定性测试-负载测试-压力测试-时间性能-空间性能

    负载测试 确定在各种工作负载下,系统各项指标变化情况

    压力测试:通过确定一个系统的刚好不能接受的性能点。获得系统能够提供的最大服务级别

    测试用例
    为特定的目的而设计的一组测试输入,执行条件和预期结果,以便测试是否满足某个特定需求。
    通过大量的测试用例来检测软件的运行效果,它是指导测试工作进行的依据。

    等价类划分法
    将不能穷举的测试过程进行合理分类,从而保证设计出来的测试用例具有完整性和代表性。
    有数据输入的地方,可以使用等价类划分法。
    从大量数据中挑选少量代表数据进行测试
    有效等价类:符合需求规格说明书规定的数据用来测试功能是否正确实现
    无效等价类:不合理的输入数据集合—用来测试程序是否有强大的异常处理能力(健壮性)
    使用最少的测试数据,达到最好的测试质量

    边界值分析法
    对输入或输出的边界值进行测试的一种黑盒测试方法。
    是作为对等价类划分法的补充,这种情况下,其测试用例来自等价类的边界。
    边界点
    1、边界是指相对于输入等价类和输出等价类而言,稍高于、稍低于其边界值的一些特定情况。
    2、边界点分为上点、内点和离点。

    如果是范围[1,100] 需要选择0,1,2,50,99,100,101
    如果是个数最多20个 [0,20] 需要测 0,10,20,-1,21

    因果图分析法
    用画图的方式表达输入条件和输出结果之间的关系。
    1 恒等
    2 与
    3 或
    4 非
    5 互斥 1个或者不选
    6 唯一 必须是1个
    7 包含 可以多选 不能不选
    8 要求 如果a=1,则要求b必须是1,反之如果a=0时,b的值无所谓
    9 屏蔽关系 当a=1时,要求b必须为0;而当a=0时,b的值不一定

    判定表法
    根据因果来制定判定表
    组成部分
    1 条件桩:所有条件
    2 动作桩: 所有结果
    3 条件项: 针对条件桩的取值
    4 动作项:针对动作桩的取值

    不犯罪,不抽烟是好男人,不喝酒是好男人,只要打媳妇就是坏男人
    条件桩 1 不犯罪 1 1 0
    2 不抽烟 1 0 1
    3 不喝酒 0 1 1
    动作桩 好男人 1 1
    坏男人 1

    场景法
    模拟用户操作软件时的场景,主要用于测试系统的业务流程
    先关注功能和业务是否正确实现,然后再使用等价类和边界值进行检测。

    基本流 正确的业务流程来实现一条操作路径
    备选流 模拟一条错误的操作流程
    用例场景要从开始到结束便利用例中所有的基本流和备选流。

    流程分析法
    流程-路径
    针对路径使用路径分析的方法设计测试用例
    降低测试用例设计难度,只要搞清楚各种流程,就可以设计出高质量的测试用例,而不需要太多测试经验

    1 详细了解需求
    2 根据需求说明或界面原型,找出业务流程的哥哥页面以及流转关系
    3 画出业务流程axure
    4 写用例,覆盖所有路径分支

    错误推断法
    利用经验猜测出出错的可能类型,有针对性列出所有可能的错误和容易发生错误的情况。
    多考虑异常,反面,特殊输入,以攻击者的态度对台程序。

    正交表
    对可选项多种可取值进行均等选取组合,最大概率覆盖测试用例
    1 根据控件和取值数选择一个合适的正交表
    2 列举取值并编号,生成取值表。
    3 把取值表与选择的正交表进行映射
    控件数
    Ln(取值数 ) 3个控件 5个取值 5的3次幂

    混合正交表
    当控件的取值数目水平不一致时候,使用allpairs工具生成

    1 等价类划分法 划分值
    2 边界值分析法 边界值
    3错误推断法 经验
    4 因果图分析法 关系
    5 判定表法 条件和结果
    6流程图法 流程路径梳理
    7 场景法 主要功能和业务的事件
    8 正交表

    先关注主要功能和业务流程,业务逻辑是否正确实现,考虑场景法
    需要输入数据的地方,考虑等价类划分法+边界值分析法,发现程序错误的能力最强
    存在输入条件的组合情况,考虑因果图判定表法
    多种参数配置组合情况,正交表排列法
    采用错误推断法再追加测试用例。

    需求分析
    场景法 分析主要功能
    输入的 等价类 边界值
    输入的 各种组合 因果图判定表
    多种参数配置 正交表
    错误推断法 经验

    软件缺陷
    软件产品中存在的问题,用户所需要的功能没有完全实现,没有满足用户的需求
    1 未达到需求规格说明书表明的功能
    2 出现了需求规格说明书指明不会出现的错误
    3 软件功能超出了需求规格说明书指明的范围
    4 软件质量不够高
    维护性 移植性 效率性 可靠性 易用性 功能性 健壮性等
    5 软件未达到软件需求规格说明书未指出但是应该达到的目标
    计算器没电了 下次还得能正常使用
    6 测试或用户觉得不好

    软件缺陷的表现形式
    1 功能没有完全实现
    2 产品的实际结果和所期望的结果不一致
    3 没有达到需求规格说明书所规定的的性能指标等
    4 运行出错 断电 运行终端 系统崩溃
    5 界面排版重点不突出,格式不统一
    6 用户不能接受的其他问题

    软件缺陷产生的原因
    需求错误
    需求记录错误
    设计说明错误
    代码错误
    兼容性错误
    时间不充足

    缺陷的信息
    缺陷id
    缺陷标题
    缺陷严重程度
    缺陷的优先级
    缺陷的所属模块
    缺陷的详细描述
    缺陷提交时间

    缺陷的严重程度划分
    1 blocker 系统瘫痪 异常退出 计算错误 大部分功能不能使用 死机
    2 major 功能点不符合用户需求 数据丢失
    3 normal 独立功能 特定调点 断断续续
    4 Trivial 细小的错误

    优先级划分
    紧急


    为什么要用索引?
    使用索引后减少了存储引擎需要扫描的数据量,加快查询速度
    索引可以把随机I/O变为顺序I/O
    索引可以帮助我们对所搜结果进行排序以避免使用磁盘临时表

    展开全文
  • airtest自动化游戏脚本测试

    千次阅读 2018-11-27 14:35:28
    官网: http://airtest.netease.com/ 文档说明: http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/index.html
    展开全文
  • 软件测试:测试流程 + 测试文档

    千次阅读 2016-08-14 20:06:51
    软件测试流程及测试文档
    1. 软件测试流程
    前期:参加需求评审会议
    拟定软件测试计划—>设计和生成测试用例—>搭建测试环境—>实施测试—>测试评估—>测试总结

    测试阶段:指定测试计划—>测试设计与开发—>执行测试—>评估总结

    2. 制定测试计划

    •测试计划阶段主要处于测试的先期准备工作阶段,在该阶段中主要是对将要进行的测试工作做整体计划安排。
    •本阶段的主要工作内容
    对需求规格说明书的仔细研究
    将要测试的产品分解成可独立测试的单元
    为每个测试单元确定采用的测试技术
    为测试的下一个阶段及其活动制定计划
    概要测试计划 + 详细测试计划 = 制定计划
    3. 测试设计与开发
    •测试设计与开发阶段包含两部分,一部分是设计,主要是参照各种相关文档对测试进行设计的工作,包括测试需求的分析和测试用例的设计;另一部分工作是开发,主要是按照设计的测试需求分析与测试用例设计的方案要求进行实施的过程,该过程包括:测试用例数据的准备,测试工具的配置、测试脚本的开发录制与维护等工作,此阶段的工作可一直持续到软件测试结束。测试用例文档是软件测试的依据,包括测试输入、测试步骤、预期结果等内容。 
    •测试用例文档的本质
    从测试的角度对被测对象的功能和各种特性的细化和展开。
    测试用例,设计测试用例 –>测试用例文档 

    •测试用例文档的好处

    保证测试功能不被遗漏,也不被重复测试。合理安排测试人员。使得软件测试不依赖于个人

    4. 实施测试
    •实施软件测试阶段主要是将设计和开发阶段的测试用例和数据应用于软件的过程,执行的过程,执行过程中发现缺陷,可提交缺陷报告。

    •缺陷报告:是软件测试过程中最重要的文档,它的内容包括:

    1)记录问题发生的环境,如:各种资源的配置情况。

    2)记录问题的在现步骤

    3)记录问题性质的说明

    4)记录问题的处理进程--问题处理进程从一定角度上反映测试的进程和被测软件的质量状况以及改善过程。

    •初测期——测试主要功能和关键的执行路径,排除主要障碍。
    •细测期——依据测试计划和测试用例,逐一测试大大小小的功能、方方面面的特性、性能、用户界面、兼容性、可用性等等;预期可发现大量不同性质、不同严重程度的错误和问题。
    •回归测试期——系统已达到稳定,在一轮测试中发现的错误已十分有限;复查已知错误的纠正情况,确认未引发任何新的错误时,终结回归测试。 

    5. 评估总结
    评审阶段是在测试结束后对整个测试过程与产品进行评估的过程。

    6. 测试文档类型


     

    展开全文
  • Android Monkey测试脚本

    万次阅读 2015-09-28 11:04:44
    Android 的 monkey test 工具提供了 -f scriptfile 参数,可以指定 test 脚本,然而翻遍了 Android 的网站也没有找到这个脚本文档,最终只在 monkey 的源码 MonkeySourceScript.java 中找到了一小段注释,里面
  • 利用EA根据sql脚本生成数据库文档

    千次阅读 多人点赞 2019-01-18 17:23:58
    最近接到一个任务,通过数据库的sql脚本生成相应的数据库文档,表的数量比较多,手动写是不划算的;几年前用过EA这款十分棒的软件,于是用EA来生成;在这次使用的时候还是有一些曲折,为了方便以后再用不用重复的去...
  • 这里介绍一种方式,实现了读取excel接口文档,生成通用的接口自动化测试用例脚本 utils.py源代码: import xlrd def parse_intf(file,sheet_index): with xlrd.open_workbook(file) as f: table = f.sheet_by_...
  • 接口测试脚本

    千次阅读 2018-11-02 23:53:38
    写了个简单的接口测试脚本,实现的功能是: 多个接口,多组参数值测试 打印url、输入参数、断言内容 断言正确就返回结果Pass,断言错误就返回结果Fail,最后统计通过和失败的用例数 数据、程序分离,详情如下:...
  • 测试脚本语言概述

    2010-06-03 10:37:00
    首先,何谓"测试脚本语言"?准确的说,"测试脚本语言"这个概念并没有确定的范围,一般是用来指在测试过程中使用到的脚本语言。那么,测试过程中使用的脚本语言到底包括哪些呢?
  • Gatling 测试脚本编写

    千次阅读 2018-09-19 15:41:26
    Gatling基于Scala开发的压测工具,我们可以通过录制自动生成脚本,也可以自己编写脚本,大家不用担心,首先脚本很简单常用的没几个,另外gatling封装的也很好我们不需要去专门学习Scala语法,当然如果会的话会更好。...
  • 自动化测试脚本技术

    千次阅读 2010-12-17 17:52:00
    自动化测试脚本技术包括:线性脚本,结构化脚本,共享脚本,数据驱动脚本与关键字驱动脚本
  • SheetJS中文文档-js导出Excel脚本

    万次阅读 2021-01-08 11:33:13
    通过官方规范、相关文档以及测试文件实现简洁的JS方法。SheetJS强调解析和编写的稳健,其跨格式的特点和统一的JS规范兼容,并且ES3/ES5浏览器向后兼容IE6。 目前这个是社区版,我们也提供了性能增强的专业版,专业版...
  • 3. 必要的测试辅助文档,如:脚本测试需求、脚本数据库文件,脚本代码文档,脚本内部数据测试报告。4. 标识本次测试脚本版本的相关配置管理描述文档。5. 脚本编辑人员每日的工作进度报告。6. 
  • 背景:公司项目java开发使用swagger工具作为接口文档,每次设计接口测试用例的时候,先是设计好excel表头,然后再一步一步的ctrl+c\ctrl+v很多重复的工作,于是想使用python来解析接口返回的json对象数据,然后清洗...
  • LoadRunner创建测试脚本

    千次阅读 2009-04-03 10:37:00
    它的脚本可以录制生成,自动关联;测试场景可以面向指标,多方监控;测试结果可以用图表显示,并且可以拆分组合。作为专业的性能测试工具,通过模拟成千上万的用户对被测系统进行操作和请求,能够在实验室环境中重现...
  • linux下expect环境安装以及简单脚本测试 网上的原贴: expect是交互性很强的脚本语言,可以帮助运维人员实现批量管理成千上百台服务器操作,很实用! expect依赖于tcl,而linux系统里一般不自带安装tcl,所以需要...
  • 如何通过postman生成接口测试脚本

    万次阅读 2017-09-21 22:16:20
    下面再给大家介绍如何通过postman生成接口测试脚本。接口测试环境:Python+requests(requests库,可以通过cmd命令窗口pip install requests安装) 我们打开postman,使用V2EX API 接口平台提供的接口去测试...
  • 接口性能测试脚本

    千次阅读 2016-08-04 15:25:17
    文件名:performance.py# -*-coding:...# 性能测试基类 import re import time import requests import threading class Performance(threading.Thread): def __init__(self, url="", method="get", header={}, body=
  • 脚本测试指的是传统意义上的包含完整测试生命周期的测试,例如:国际软件测试认证委员会(InternationalSoftware Testing Qualifications Board,缩写为ISTQB)提出的包含五个测试阶段的测试生命周期(测试计划和...
  • 用 JavaScript 编写测试脚本

    千次阅读 2018-07-04 17:45:04
    用 JavaScript 编写测试脚本 Truffle使用 Mocha 测试框架和 Chai 断言,为编写 JavaScript 测试提供了坚实的框架。 让我们深入研究,看看 Truffle 是如何建立在 Mocha 之上,让测试你的合约变得轻而易举。 注意...
  • 基于selenium的自动化测试脚本编写-python

    万次阅读 多人点赞 2017-06-30 15:34:45
    基于selenium的自动化测试脚本编写-python
  •  有的时候,写脚本之前会犹豫,那么多脚本语言,用哪种好呢?常常是用某种写好了,过了些天觉得还是用另一种好,结果又写一遍。我想问题的关键在于很好的区别各种语言的优缺点,根据各自的特点觉得其适合
  • 开发自动化测试脚本的技巧和心得

    千次阅读 2007-07-20 12:44:00
    本文提供了许多在文档测试脚本,调试测试脚本,执行测试脚本的同行评审和同步测试脚本方面的建议。增量式调试脚本录制测试脚本,和其他的软件开发成果一样,会变得非常大。为了可以成功的回放,需要调试几百行的...
  • Android自动化测试之Monkey命令使用及monkey脚本编写

    万次阅读 多人点赞 2018-06-29 16:22:14
    系列文章 Android自动化测试环境部署及adb sdkmanager avdmanager ...Android自动化测试之MonkeyRunner MonkeyDevice MonkeyImage API使用详解 脚本编写 脚本录制回放 背景 如果开发完app,开发人员没有充分的...
  • Mysql压力测试shell脚本

    千次阅读 2013-02-28 22:52:13
    Mysql自带了压力测试工具mysqlslap,所以我们可以不用自己编写程序来测试Mysql读取的压力。压力测试shell脚本如下: #!/bin/sh while true do mysqlslap --concurrency=100 --iterations=10 --create-schema='
  • ETL测试参考文档

    千次阅读 2010-09-19 17:45:00
    ETL测试参考文档   本文档主要介绍ETL测试的流程,以及一般的项目情况来说明ETL的测试方法。 ETL测试流程图 <br /> 测试环节 1、需求分析 熟悉业务流程和业务规则,根据...
  • Jmeter快速创建接口测试脚本

    千次阅读 2019-09-12 19:40:55
    通过Jmeter工具进行接口测试。 使用此接口之前,需要在聚合平台进行注册,下单申请接口,大多数接口都有免费次数,非常方便大家学习接口测试。 操作流程 启动Jmeter 添加“线程组” 右击“测试计划”,选择...
  • LR接口压力测试脚本范例

    千次阅读 2015-12-18 16:51:44
    做过一段时间的接口测试任务,下面把一段接口测试脚本分享出来: Action() {    int rev=0;    web_service_call( "StepName=getPlanData_104",  "SOAPMethod=PlanDataServiceService|PlanDa
  • 自动化冒烟测试脚本应当遵循的原则:1、覆盖主要功能;冒烟测试不是系统测试或集成测试,所以不需要面面俱到,重点放在保证主要功能或主要业务路径执行正常;2、易用性;既然是自动化测试脚本,那么最好的状况是只...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 263,968
精华内容 105,587
关键字:

脚本测试文档