Codeception 测试 Php 代码

一、一句话概述

使用 cc 进行单元测试,保证现有代码质量,为以后维护与重构提供支撑。

二、目标

  • 安装配置 cc
  • 编写测试代码,简化开发与最大化稳定性和可维护性

三、测试的类型

1. 单元测试(UT)

  • 执行一段与其他代码完全隔离的代码单元
  • 断言代码行为
  • 描述用例的预期

2. 功能测试(FT)

  • 执行应用(客户端)请求
  • 断言返回值
  • 描述应用预期行为
  • 依赖框架

3. Web 服务测试

  • 通过 http client 执行 api 请求
  • 对 api 返回值断言
  • 描述 api 行为

四、系统要求

  • PHP 5.4+
  • json 扩展
  • mbstring 扩展
  • xdebug 扩展(生成覆盖率报告)

下载安装配置 php扩展 xdebug

版本选用最新稳定版: 2.6.1

  1. wget https://pecl.php.net/get/xdebug-2.6.1.tgz
  2. tar xvf xdebug-2.6.1.tgz
  3. cd xdebug-2.6.1
  4. /path/to/phpize
  5. ./configure --enable-xdebug
  6. make
  7. make install
  8. php.ini 文件中添加此行:
  9. zend_extension="/wherever/you/put/it/xdebug.so"
  10. 查看是否安装上:
  11. php -i | grep xdebug

五、安装配置与运行 Codeception

  1. wget http://codeception.com/codecept.phar # 不需要解压
  2. php codecept.phar help # 帮助文档
  3. php codecept.phar bootstrap # 创建 test & codeception.yml 到当前文件夹
  4. php codecept.phar build # 通过配置生成必要的类,每次修改配置都需要运行次命令
  5. php codecept.phar run {suite_name} # 不跟 suite name 即是运行全部
  6. php codecept.phar run api ThingTest.php
  7. php codecept.phar run api ThingTest.php:method

配置与代码示例

地址:https://github.com/wdy1184/CodeceptionExampleWithYii2

六、测试代码格式

codeception 提供了两种写测试代码的方式

  1. Cept: 一个独立的测试文件。可以写一套测试流程。
  2. Cest: 一个类文件,多个测试写在一个类中。
  3. PHPUnit: 支持 phpunit 原生测试框架的格式

七、与Yii2集成

配置:

_bootstrap.php 代码

  1. <?php
  2. defined('APP_PATH') or define('APP_PATH', dirname(dirname(__FILE__)));
  3. // 注册 Composer 自动加载器
  4. // 包含 Yii 类文件
  5. require(__DIR__ . '/../../psservice/libs/envFun.php');
  6. require(__DIR__ . '/../../vendor/autoload.php');
  7. // 环境变量配置
  8. defined('YII_DEBUG') or define('YII_DEBUG', getYaconfEnv('YII_DEBUG'));
  9. defined('YII_ENV') or define('YII_ENV', getYaconfEnv('YII_ENV'));
  10. defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', getYaconfEnv('YII_ENABLE_ERROR_HANDLER'));
  11. require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
  12. // 暂时向下兼容老文件
  13. require(__DIR__ . '/../../config/errorCode.php');
  14. require(__DIR__ . '/../../config/constant.php');
  15. Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');
  16. Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');
  17. Yii::setAlias('@psservice', dirname(dirname(__DIR__)) . '/psservice');
  18. Yii::setAlias('@config', dirname(dirname(__DIR__)) . '/config');
  19. $config = require(APP_PATH . '/../config/web.php');

Cest 代码示例

  1. <?php
  2. namespace tests\api;
  3. use backend\models\user\EduOcrTourist;
  4. use backend\models\user\EduOcrUser;
  5. use backend\modules\user\services\UserEntity;
  6. use config\UserCacheKey;
  7. use psservice\models\user\WxEduUser;
  8. use tests\ApiTester;
  9. class UserControllerCest
  10. {
  11. private $token;
  12. public function _before(ApiTester $I)
  13. {
  14. }
  15. /**
  16. * 用户登录
  17. * @param ApiTester $I
  18. */
  19. public function actionLoginTest(ApiTester $I)
  20. {
  21. $mobile = '18510473853';
  22. $token = $I->amUserLogin($mobile);
  23. $I->sendCommandToRedis('select', getPsYaconfEnv('PS_OCR_SINGLE_REDIS_DATABASE_1'));
  24. $userRedisInfo = $I->grabFromRedis(sprintf(UserCacheKey::USER_INFO, $token));
  25. $userRedisInfo = json_decode($userRedisInfo, 1);
  26. codecept_debug($userRedisInfo);
  27. $I->assertGreaterThan(0, $userRedisInfo['userId']);
  28. $I->assertEquals(0, $userRedisInfo['touristUserId']);
  29. $I->assertEquals(UserEntity::$roleMap['user'], $userRedisInfo['role']);
  30. $I->assertEquals($mobile, $userRedisInfo['mobile']);
  31. $I->assertLessOrEquals(10, (time() - strtotime($userRedisInfo['loginTime'])));
  32. $I->seeRecord(WxEduUser::className(), ['UserId' => $userRedisInfo['commonUserId']]);
  33. }
  34. /**
  35. * 游客登录
  36. * @param ApiTester $I
  37. */
  38. public function actionTouristLoginTest(ApiTester $I)
  39. {
  40. $token = $I->amTouristLogin('6CB7B05E-DE62-415A-96CF-6D57B666F939');
  41. $I->sendCommandToRedis('select', getPsYaconfEnv('PS_OCR_SINGLE_REDIS_DATABASE_1'));
  42. // $userRedisInfo = $I->grabFromRedis(sprintf(UserCacheKey::TOURIST_INFO, $token)); // 机缘巧合下,没有使用 touristInfo 常量
  43. $userRedisInfo = $I->grabFromRedis(sprintf(UserCacheKey::USER_INFO, $token));
  44. $userRedisInfo = json_decode($userRedisInfo, 1);
  45. codecept_debug($userRedisInfo);
  46. $I->assertGreaterThan(0, $userRedisInfo['id']);
  47. $I->seeRecord(EduOcrTourist::className(), ['deviceId' => '6CB7B05E-DE62-415A-96CF-6D57B666F939']);
  48. }
  49. /**
  50. * 登出
  51. * @param ApiTester $I
  52. */
  53. public function actionLogoutTest(ApiTester $I)
  54. {
  55. $this->token = $I->amUserLogin('18510473853');
  56. $I->sendPOST('/user/user/logout', [
  57. 'token' => $this->token,
  58. 'deviceId' => '6CB7B05E-DE62-415A-96CF-6D57B666F939',
  59. ]);
  60. $I->seeResponseContainsJson(['code' => 99999]);
  61. }
  62. /**
  63. * 登录用户信息
  64. */
  65. public function actionInfoTest(ApiTester $I)
  66. {
  67. $mobile = '18510473853';
  68. $this->token = $I->amUserLogin($mobile);
  69. $I->sendGET('/user/user/info', [
  70. 'token' => $this->token,
  71. ]);
  72. $ret = json_decode($I->grabResponse(), 1);
  73. $I->seeRecord(EduOcrUser::className(), ['id' => $ret['data']['userId'], 'mobile' => $mobile]);
  74. }
  75. }

八、增加自定义方法

tests/_support/Helper/{suite_name}.php

在其中增加方法后,可以通过 ApiTester 的对象 $I 进行调用。

九、运行测试代码生成测试报告(代码覆盖率)

  1. php codecept.phar run -coverage --coverage-html --coverage-xml

测试报告会在 tests/_output/coverage/ 中生成。

通过设定 nginx 静态服务器,可以查看测试报告。如:

十、利用测试用例进行重构

步骤:

  1. 编写测试代码
  2. 持续维护测试代码,提高测试代码覆盖率
  3. 运行测试代码使其通过。并不断重复 1、2、3 步骤
  4. 代码库越来越庞杂
  5. 重构
    1. 合并相似循环、方法、类
    2. 提取方法
    3. 等等
  6. 运行测试用例,找到失败的用例
  7. 修改重构后的代码,使其通过测试用例,并重复 5、6、7 步骤

十一、利用测试用例,快速了解新代码

steps 会在执行的时候打印出执行的断言,有助于观察测试用例做了什么,以及怎么使用你写的接口。

即测试用例是 : self-document 的。

  1. php codecept.phar run api --steps # steps 会在执行的时候打印出执行的断言

Codeception 实战的更多相关文章

  1. SSH实战 · 唯唯乐购项目(上)

    前台需求分析 一:用户模块 注册 前台JS校验 使用AJAX完成对用户名(邮箱)的异步校验 后台Struts2校验 验证码 发送激活邮件 将用户信息存入到数据库 激活 点击激活邮件中的链接完成激活 根 ...

  2. GitHub实战系列汇总篇

    基础: 1.GitHub实战系列~1.环境部署+创建第一个文件 2015-12-9 http://www.cnblogs.com/dunitian/p/5034624.html 2.GitHub实战系 ...

  3. MySQL 系列(四)主从复制、备份恢复方案生产环境实战

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  4. Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  5. 给缺少Python项目实战经验的人

    我们在学习过程中最容易犯的一个错误就是:看的多动手的少,特别是对于一些项目的开发学习就更少了! 没有一个完整的项目开发过程,是不会对整个开发流程以及理论知识有牢固的认知的,对于怎样将所学的理论知识应用 ...

  6. asp.net core 实战之 redis 负载均衡和"高可用"实现

    1.概述 分布式系统缓存已经变得不可或缺,本文主要阐述如何实现redis主从复制集群的负载均衡,以及 redis的"高可用"实现, 呵呵双引号的"高可用"并不是 ...

  7. Linux实战教学笔记08:Linux 文件的属性(上半部分)

    第八节 Linux 文件的属性(上半部分) 标签(空格分隔):Linux实战教学笔记 第1章 Linux中的文件 1.1 文件属性概述(ls -lhi) linux里一切皆文件 Linux系统中的文件 ...

  8. Linux实战教学笔记07:Linux系统目录结构介绍

    第七节 Linux系统目录结构介绍 标签(空格分隔):Linux实战教学笔记 第1章 前言 windows目录结构 C:\windows D:\Program Files E:\你懂的\精品 F:\你 ...

  9. Linux实战教学笔记06:Linux系统基础优化

    第六节 Linux系统基础优化 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 基础环境 第2章 使用网易163镜像做yum源 默认国外的yum源速度很慢,所以换成国内的. 第一步:先备份 ...

随机推荐

  1. 你可能不知道的 docker 命令的奇淫怪巧

    你可能不知道的 docker 命令的奇淫怪巧 Intro 介绍并收录一些可能会用到的一些简单实用却很少有人用的 docker 命令 dangling images build 自己的 docker 镜 ...

  2. 删除表中多余的重复记录,重复记录是根据单个字段(Id)来判断,只留有rowid最小的记录

    delete from Resource where Title in (select Title from Resource group by Title having count(Title) & ...

  3. Sql基础(零基础学数据库_SqlServer版)

    刚开始学C#时候的笔记,只是些基础的语句如有错误请批评指正,谢谢,(使用SqlServer2012以上) 一.  数据库概述 SQI全称 structrued Query Language 1.数据: ...

  4. spring集成shiro登陆流程(下)

    首先声明入门看的张开涛大神的<跟我学shiro> 示例:https://github.com/zhangkaitao/shiro-example 博客:http://jinnianshil ...

  5. select下拉框的探索(<option></option>标签中能嵌套使用其它标签吗)

      select标签大家应该经常用到,有个问题可能没怎么注意过, select标签里面的option标签可以嵌套其它标签吗,比如i,span标签等? 经本人测试,答案是:可以嵌套其它标签,但审查元素会 ...

  6. Hadoop系列002-从Hadoop框架讨论大数据生态

    本人微信公众号,欢迎扫码关注! 从Hadoop框架讨论大数据生态 1.Hadoop是什么 1)Hadoop是一个由Apache基金会所开发的分布式系统基础架构 2)主要解决,海量数据的存储和海量数据的 ...

  7. AI - TensorFlow - 示例01:基本分类

    基本分类 基本分类(Basic classification):https://www.tensorflow.org/tutorials/keras/basic_classification Fash ...

  8. Spring Cloud实战的代码和视频位置

    大家好,本博文的连接里包含了Spring Cloud实战的代码和视频位置. 代码下载连接: 视频下载连接:

  9. 查看ntp时间是否同步

    nptq -p 命令 ntpq用来监视ntpd操作,ntpq -p查询网络中的NTP服务器,同时显示客户端和每个服务器的关系,也可以用watch实时查看. 位置 标志 含义 remote之前 * 响应 ...

  10. docker-compose-volumes的说明

    docker-compose里两种设置方式都是可以持久化的 绝对路径的 ghost: image: ghost volumes: - ./ghost/config.js:/var/lib/ghost/ ...