您的当前位置:首页>全部文章>文章详情

【PHP】php逻辑测试,测试也要设计—phpunit实践

CrazyPanda发表于:2024-06-22 17:27:27浏览:267次TAG: #php #测试

概述本文阐述如何利用面向对象的思想,在phpunit框架下实现测试用例、数据文件、配置信息和lib库等信息分离,并能有效组合。

也许有些QA认为,测试代码只要能满足测试要求即可,根本不需要有什么设计的理念。其实不然,好的测试代码,应该是可读性强,可扩展性强。以下分享一个我在实际项目中的小想法来阐述这个观点,仅作抛砖引玉之用。

具体实现在autoFunc测试目录下,创建conf、data、lib三个目录,分别用于保存配置信息、数据文件和lib库,测试用例直接放在autoFunc下。

0795c09576b7458e5dda3f0594116905.png

A 方案直接在test_object_put_get.php中require config.php和util.php,如下:

require_once 'conf/config.php';

require_once 'lib/util.php';

似乎很简单,但这里却有两个问题,phpunit的测试用例,是以class继承PHPUnit_Framework_Testcase类的方式来组织的,这样在testcase中就无法访问config.php的变量列表,或许你会说,这个很好解决呀,直接的testcase的class中指定config.php的变量名为global类型即可,如下:

7aa835178382461379735e3cbe6b2aa3.png

弊端1:config.php中有几个配置,testcase中就需要global几个,不免太手工了。

弊端2:util.php中function无法通过global方式声明,在testcase中也就无法访问到。

B 方案鉴于A 方案的局限性,我提出了面向对象的方式,来实现配置信息、测试用例、lib库有效的隔离。B 方案最突出的地方在于引入了继承的概念,容我一一道来。

config.php测试用例中总有一些常量会被反复使用到,使用配置文件的方式是所有测试人员的共识,在这里,我做了一个小小改善,将config信息包装为类的方式,以便于在lib库的类中使用到,如下:

class Config{
public $WEB_SITE = "http://test.com:8090/";
public $BUCKET = "vincent";
public $ACCESS_KEY = "jhPLaYVh11wo";
public $SECURE_KEY = "A23mtEjHwv1z";
public $SIGN_FLAG="TST";
public $DATA_DIR = "./data/";
//no use now
public $IP = "";
public $TIME = "";
}

util.php编写测试用例时,总会有一些逻辑处理片段反复的出现,这造成了测试代码的大量冗余,也加大了维护成功,将这些逻辑处理封装为一个个函数是第一个步,之后将通用的函数抽取为lib库的形式,而那些函数适合抽取为lib库。根据经验我列举几个lib的共有特性:

1.与测试用例逻辑无关

2.完成单一职责

3.可被其他用例共用

如果满足这三个条件,那这个函数就可以抽取为lib库,如下文的signature签名函数。

在实际项目中,我抽取了部分函数为lib库,并将lib库也封装为类的形式,同时继承于class Config,如下:

require_once 'conf/config.php';
class Util extends Config{
/**
* 返回签名串
* @param string $method
* @param string $object
*/
public function signature($method, $object){
$content = "$this->SIGN_FLAG\nMethod=$method\nBucket=$this->BUCKET\nObject=$object\n";
if($this->IP != ""){
$content .= "Ip=".$this->IP."\n";
}
if($this->TIME != ""){
$content .= "Time=".$this->TIME."\n";
}
$sign = "?Sign=$this->SIGN_FLAG:$this->ACCESS_KEY";
return $sign;
}
}

注意:这里Util类继承于Config类,也就继承了Config类中的所有成员变量,故在Util类中可以直接通过$this指针直接访问到配置信息。

TEST CASEStestcases中通过在setUp()函数中new一个Util对象,这样就可以轻松使用lib库中所有方法了,如下:

require_once 'PHPUnit/Framework.php';
require_once 'lib/util.php';
class ObjectPutGET extends PHPUnit_Framework_Testcase{
protected $util;
protected function setUp(){
$this->util = new Util();
}
public function testNormal(){
$object = '/normalObj';
$sign = $this->util->signature("PUT", $object);
$url = $this->util->WEB_SITE.$this->util->BUCKET.$object.$sign;
$http = curl_init();
$infile = fopen("data/file1", "r");
curl_setopt($http, CURLOPT_URL, $url);
curl_setopt($http, CURLOPT_INFILE, $infile);
curl_setopt($http, CURLOPT_INFILESIZE, 8);
curl_setopt($http, CURLOPT_UPLOAD, 1);
curl_exec($http);
curl_close($http);
fclose($infile);
}
}

这里testcases中有一个protect的成员变量$util,并在setUp()中初始化,这样在每个testcase中都可以使用$this->util来访问Util类中的所有方法和变量了。

问题:测试中可能遇到这样的问题,lib库的function依赖于config中的配置,在测试用例中调用function时,又希望能用不同的参数。

解决:按照gtest的测试经验,需要为function提供额外的参数,供传入不同的值。既然使用面向对象了,这里就简单了,只需要通过实例化的lib库调用$this->util->配置项,直接更改配置项信息。如果希望封装好点,可以设置get、set方法分别用于配置项的get和set。

数据驱动将测试数据保存到data/目录下的相应文件中,通过php unit的dataprovider机制与测试代码结合,将测试数据与用例逻辑解耦合,增加case只需要相应增减数据文件,不需要变更用例逻辑,降低维护成本,提高可扩展性。

/**
* dataprovider for testObjectFileType
*/
public function fileType(){
return array(
array("fputtype.txt", "/putfiletype.txt"),
array("fputtype.docx", "/putfiletype.docx"),
array("fputtype.pdf", "/putfiletype.pdf"),
array("fputtype.xls", "/putfiletype.xls"),
array("fputtype.mp3", "/putfiletype.mp3"),
array("fputtype.mkv", "/putfiletype.mkv"),
array("fputtype.rar", "/putfiletype.rar")
);
}
/**
* 文件,文件类型为txt word excel pdf mp3 mkv rar
* @dataProvider fileType
*/
public function testObjectFileType($fileType, $object_name){
$fileName = $this->util->DATA_DIR.$fileType;
$obj = $object_name;
//put object
$result = $this->util->putObject($fileName, $obj);
$this->assertEquals("", $result);
//get object
$result = $this->util->getObject($obj);
//check
$expect = md5(file_get_contents($fileName));
$actual = md5($result);
$header = new Header(file_get_contents($this->util->HEADER_FILE));
$etag = $header->getETag();
$this->assertEquals($expect, $actual);
$this->assertEquals($expect, $etag);
}

这样组织后,测试用例、配置信息、数据文件以及lib库就解耦了,不管修改哪部分,都可以直接找到并修改,不用担心会对其他case造成什么影响。

A. 编写测试用例,在测试用例根目录下找到对应测试文件,增减相应的case逻辑即可,并且可以在测试用例中轻松调用lib库,动态修改配置信息。

B. 修改数据文件?两步即可,在data目录下增减数据文件,修改对应测试用例的数据驱动信息。

C. 在conf目录中修改配置信息,由于配置信息是全局的,修改已有配置信息需要慎重。

D. lib库与conf一样是全局可见的,修改已有function需要考量对其他case有没有影响。

总结不仅仅RD的代码需要可扩展性,QA的测试代码同样也需要。

测试也需要设计。

猜你喜欢

【PHP】php二维数组排序
        PHP作为一种常用的Web编程语言,在不同的应用场景下,对数组的处理是不可避免的。而对于数组排序,也是开发时经常面对的一个问题。本文将介绍如何对二维数组进行排序。一、二维数组排序概述在PHP中,二维数组是由多个一维数组链接而成的复合数组,也就是说,它不仅有行的概念,还有列的概念。当需要对二维数组进行排序时,通常需要对其中某一列进行排序。这时候需要使用PHP提供的函数来进行排序。二、对二维数组进行排序的方法1.使用usort()函数u
发表于:2023-12-14 浏览:386 TAG:
【PHP】PHP框架在大型电子商务和SaaS平台中的应用场景和挑战
hp框架在大型电子商务和saas平台中广泛用于构建购物车、用户管理和数据处理等功能。应用场景包括但不限于电子商务平台的购物车和支付网关构建,以及saas平台的用户管理和数据存储功能开发。然而,这些平台也面临着性能、可扩展性、安全和维护方面的挑战。PHP框架在大型电子商务和SaaS平台中的应用场景和挑战简介PHP是一种广泛使用的后端编程语言,凭借其灵活性、广泛的库支持和易于使用性,在大型电子商务和SaaS平台中备受青睐。本文将探讨这些平台中PHP框架的应用场景和面临的挑战。应用场景电子商务:构建
发表于:2024-05-30 浏览:274 TAG:
【PHP】PHP8.1新特性大讲解之initializers初始化器
PHP 8.1:初始化器(new in initializers)PHP 8.1 添加了一个看似很小的细节,但我认为它会对许多人产生重大的日常影响。那么这个“初始化器 RFC 中的新内容”是关于什么的?我们来看一个例子;我们都写过这样的代码:class MyStateMachine {     public function __construct(       &n
发表于:2024-01-04 浏览:320 TAG:
【PHP】php如何去掉数组内重复元素
php去掉数组内重复元素的方法:1、使用“array_unique()”函数,去除数组中的重复数据;2、通过foreach循环遍历,通过定义一个新的数组存储不重复的数据的方法实现去重;3、使用array_flip()和array_keys()函数,可得到去重后的数组;4、使用array_filter()函数,通过使用该函数结合匿名函数的方式对原始数组进行去重。本教程操作系统:Windows10系统、PHP8.1.3版、Dell G3电脑php去除数组内重复元素的方法方法一:使用arra
发表于:2023-12-20 浏览:339 TAG:
【PHP】php使用curl常见出错
hp是一款广泛应用于服务器端开发的编程语言。在常见的web应用程序中,php常使用curl库实现http请求,主要用于与其他web服务进行通信。然而,在使用curl时,开发人员可能会遇到各种问题,其中最常见的问题是curl在发送请求时出错。cURL错误通常会导致请求无法正常发送或无法成功获取响应。本文将介绍cURL的常见错误以及如何解决这些问题。一、未安装cURL扩展在使用cURL前,首先需要在PHP中安装cURL扩展,否则cURL库将无法正常工作,在发送请求时会抛出错误。要检查PHP是否已经
发表于:2024-03-19 浏览:304 TAG:
【PHP】php 二维数组删除
在PHP中,删除二维数组通常需要通过使用循环和条件语句来实现。在实际编程中,我们通常会遇到以下两种情况需要删除二维数组:删除指定元素:需要找到要删除的元素所在的子数组,并从该子数组中删除指定元素。删除整个子数组:需要找到包含要删除的子数组的父数组,并将该子数组删除。下面我们将分别介绍这两类任务的具体实现方法。方法一:删除指定元素要删除一个指定的元素,我们需要通过循环遍历所有子数组,找到包含目标元素的子数组,并将其从该子数组中删除。以下是实现该过程的代码示例:// 定义一个二维
发表于:2023-12-20 浏览:329 TAG:
【PHP】php有哪些高效文本数据库
php高效文本数据库有SQLite、Redis和MongoDB等。详细介绍:1、SQLite是一种嵌入式的关系型数据库,它以文件形式存储数据,不需要独立的服务器进程,SQLite在PHP中有广泛的应用,它提供了高效的数据存储和检索能力,支持常见的SQL语法和事务操作,SQLite具有小巧、快速、可靠和易于集成的特点,适用于小型项目或需要单用户访问的应用程序;2、Redis等等。本教程操作系统:windows10系统、PHP 8.1.3版本、DELL G3电脑。在PHP中,有几种高效的文
发表于:2023-12-28 浏览:321 TAG:
【PHP】php8的扩展arginfo生成工具之使用初体验
hp8提供了非常方便的扩展函数或类参数信息的生成工具。只需要维护一份xyz.stub.php,就可以使用工具生成 xyz_arginfo.h。毫无疑问,这种方式,又降低了广大 phper 开发扩展的门槛,更易维护。上手体验:生成扩展骨架。cd ext php ext_skel.php --ext test随便添加一个函数,更改 test.stub.php。<?php   /** @generate-function-entrie
发表于:2024-01-01 浏览:292 TAG:
【PHP】使用ThinkPHP6和Swoole实现的RPC服务与微服务架构整合
随着互联网技术的发展,提高系统的可扩展性和性能成为了一个重要的课题。为了满足这种需求,将RPC服务与微服务架构进行整合成为了一种较为常见的解决方案。本文将介绍如何使用ThinkPHP6和Swoole实现RPC服务与微服务架构的整合,同时提供具体的代码示例。一、RPC服务简介RPC(Remote Procedure Call,远程过程调用)是一种使调用者能够像调用本地函数一样调用远程函数的技术。它的原理是在客户端和服务端之间建立一个通信通道,在客户端发出调用请求后,服务端执行相应的逻辑并将结果返回
发表于:2024-07-29 浏览:311 TAG:
【PHP】php中?:与??运算符有什么不同?
在PHP 7中,有两个类似的语法结构:“??”和“?:”,它们都是用于处理条件判断和返回值的运算符。尽管它们看起来相似,但它们的作用和用法有一些区别。"?:"是三目运算符,语法格式为:$result = $test ? $test : ′ ′ ; 意思就是当test存在时(即empty($test)为false),则返回它本身,否则返回空(当然也可以返回其他,这里的空只是举个例子)。"??"是php7新引入的语法,它相当于是isset($result[‘k
发表于:2024-08-01 浏览:279 TAG: