接口测试方案之 PHPUnit + Jenkins + Ant

Web API 自动化测试,此次我选择了 PHPUnit(之前也用过 MSTest,Junit,TestNG 等),因为现在公司产品的开发语言是 PHP。我倾向于保持和开发使用一样的语言,好处我觉得有以下几个:

1. 你可以更加充分的了解这个开发语言,有可能还会踩一些坑

2. 如果你有权限的话你也可以 review 代码

3. 如果遇到什么非常难解决的问题,大牛就在你身边

4. 和开发交流的时候更加专业,甚至还可以提出一些好的建议

项目背景:

产品是完全基本 API 接口的支付系统,只有高度的自动化才能确保快速迭代和发布。

安装:

以 Mac OS 为例

$ wget http://phar.phpunit.cn/phpunit-6.2.phar

$ chmod +x phpunit-6.2.phar

$ sudo mv phpunit-6.2.phar /usr/local/bin/phpunit

$ phpunit --version

在新系统中,/usr/bin 成为了系统保护目录,所以我们以前使用的替换 系统 php 的方法行不通了,既然行不通,那就升升级,就用它自带的 php。

$ curl -s http://php-osx.liip.ch/install.sh | bash -s 7.1

$ vi ~/.bash_profile

加入一行

$ PATH=/usr/local/php5/bin:$PATH

$ export PATH

编写自动化脚本:

根据自身项目特点,选择数据驱动、关键字渠道或者混合驱动,这里以数据驱动为例:

开启项目自动化测试之前一定要先思考,万不可上来就开始编码,一定要先弄清楚一些问题。产品有什么特点?希望框架达到什么效果?覆盖率要达到多少?有没有现成的框架可以使用?能不能满足要求?如何组织脚本结构?哪些需要定义为静态变量?哪些要定义为公共 function?等等,想清楚了这些再去实践再去验证。(我一直认为思维是最重要的)

框架结构:

我这里是用 PHPUnit 编写测试脚本,用 xml 配置以便于执行 test suites(存放在 tests 目录),这些 PHPUnit 都有文档示例可以参考。简单示例如下:

测试脚本示例:

namespace APITest;

use PHPUnit\Framework\TestCase;

require_once '../init.php';

class APITest extends TestCase
{
    public $name, $appId;

    function setUp()
    {
        Util::setAPIEnv();
        $this->appId = Pingpp::$appId;
    }

    /**
     * 仅必填字段
     * @throws \Exception
     */
    function testCreateXXWithRequiredFields()
    {
        $data = array(
            'type' => 2,
            'percent_off' => 90
        );
        $createRes = Util::curlHttpRequest(false, true, true, true, 'POST', '/xx/xx', $data);
        $this->assertEquals($data['type'], $createRes['type']);
        $this->assertEquals($data['percent_off'], $createRes['percent_off']);
    }
}

phpunit.xml 配置示例:

<phpunit
        beStrictAboutTestsThatDoNotTestAnything="false">
    <!--PHPUnit 可以更严格对待事实上不测试任何内容的测试,beStrictAboutTestsThatDoNotTestAnything 默认为 true,如果某个测试未进行任何断言,它将被标记为有风险,这里我选择关闭-->
    <php><!--<php> 元素及其子元素用于配置 PHP 设置、常量以及全局变量。同时也可用于向 include_path 前部置入内容-->
        <includePath>.</includePath>
        <var name="apiBaseURL" value="https://api.xxx.com"/>
        <var name="isLiveMode" value='true'/>
    </php>
    <testsuites><!--带有一个或多个 <testsuite> 子元素的 <testsuites> 元素用于将测试套件及测试用例组合出新的测试套件。-->
        <testsuite name="API Test Suite">
            <file>../tests/GoodsTest.php</file><!--具体的 Test Class-->
            <file>../tests/APITest.php</file>
        </testsuite>
    </testsuites>
    <!--You can just involve "include" or "exclude" in groups-->
    <groups><!--<groups> 元素及其 <include>、<exclude>、<group> 子元素用于从带有 @group 标注的测试中选择需要运行(或不运行)的分组-->
        <!--<include>-->
        <!--<group>id</group>-->
        <!--</include>-->
        <exclude>
            <group>refund</group>
        </exclude>
    </groups>
    <logging><!--<logging> 元素及其 <log> 子元素用于配置测试执行期间的日志记录-->
        <log type="junit" target="../logs/junit.xml" logIncompleteSkipped="false"/>
        <log type="testdox-html" target="../logs/testdox.html"/>
    </logging>
</phpunit>

为了和 Jenkins 集成,这里选择 Ant 配置 build.xml,示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<project name="APITest" default="test_Live">

    <property name="basedir" value="." />
    <target name="test_Live"
            depends="setup,DataTime,testLive"/>

    <target name="DataTime">
        <tstamp>
            <format property="dt" pattern="yyyyMMddHHmmss"/>
        </tstamp>
        <!--以下的几个属性是系统自带的,初始了tstamp之后,它们就有值了
        <echo message="System:"/>
        <echo message="DSTAMP = ${DSTAMP}"/>
        <echo message="TSTAMP = ${TSTAMP}"/>
        <echo message="TODAY = ${TODAY}"/>
        <echo message="dt = ${dt}"/>-->
    </target>
    <target name="setup">
        <delete dir="${basedir}/logs" />
    </target>
    <target name="testLive">
        <exec executable="${exectest}" dir="${basedir}/tests">
            <arg line="--log-junit ../logs/junit.xml --configuration pingpp_Live.xml"/>
        </exec>
    </target>
</project>

代码通过 gitlab 管理。

Jenkins 配置,配置的 snapshot 就不贴了。

至此,可以运行了,并且看起来是那么的顺利,然后随着时间的推移,产品越来越复杂,接口越来越多,脚本成千上万,其执行时间也变得越来越长,这显然已经违背了自动化的特质——快,所以我们选择引入了 paratest 来帮助我们重返速度与激情。paratest 有两种并行运行方式,-p 是 test class 级别的,即不同的 test class 可以并行执行,而每一个 test class 内还是顺序执行; -f 是 test method 级别的,即在一个 test class 内,不同的 method 并行运行。

build.xml 修改配置如下,3 个 parallel processes

<property name="exectest" value="../vendor/bin/paratest"/><!--并行执行测试-->
<property name="exectest_Args" value="-p3 --log-junit ../logs/junit.xml"/><!--并行执行测试需要的参数-->
<target name="testLive">
<exec executable="${exectest}" dir="${basedir}/tests">
    <arg line="${exectest_Args}"/>
    <arg line="--configuration pingpp_Live.xml"/>
</exec>
</target>

为了可以达到多个 test class/method 并行执行的目的,这里一定要注意以下几点:

  1. 任何两个 Test Class 间最好不要有公共依赖项,尤其是那些可能会被修改且需要 assert 的值
  2. 任何两个 Test Method 间最好不要有公共依赖项,尤其是那些可能会被修改且需要 assert 的值

当然,不同的产品其测试脚本要满足上面的条件的难易程度区别是很大的,但是一定要朝这个方向努力,反复分析业务场景,从自动化框架着手,以多种形式组合脚本,争取至少在其中一种并行测试时不会出现因依赖项被修改而报错。

测试的关键是思维方式,一定要培养自己的测试思维。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值