Codeception 实战
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
wget https://pecl.php.net/get/xdebug-2.6.1.tgz tar xvf xdebug-2.6.1.tgz cd xdebug-2.6.1 /path/to/phpize ./configure --enable-xdebug make make install 在 php.ini 文件中添加此行: zend_extension="/wherever/you/put/it/xdebug.so" 查看是否安装上: php -i | grep xdebug
五、安装配置与运行 Codeception
wget http://codeception.com/codecept.phar # 不需要解压 php codecept.phar help # 帮助文档 php codecept.phar bootstrap # 创建 test & codeception.yml 到当前文件夹 php codecept.phar build # 通过配置生成必要的类,每次修改配置都需要运行次命令 php codecept.phar run {suite_name} # 不跟 suite name 即是运行全部 php codecept.phar run api ThingTest.php php codecept.phar run api ThingTest.php:method
配置与代码示例
地址:https://github.com/wdy1184/CodeceptionExampleWithYii2
六、测试代码格式
codeception 提供了两种写测试代码的方式
- Cept: 一个独立的测试文件。可以写一套测试流程。
- Cest: 一个类文件,多个测试写在一个类中。
- PHPUnit: 支持 phpunit 原生测试框架的格式
七、与Yii2集成
配置:

_bootstrap.php 代码
<?php defined('APP_PATH') or define('APP_PATH', dirname(dirname(__FILE__))); // 注册 Composer 自动加载器 // 包含 Yii 类文件 require(__DIR__ . '/../../psservice/libs/envFun.php'); require(__DIR__ . '/../../vendor/autoload.php'); // 环境变量配置 defined('YII_DEBUG') or define('YII_DEBUG', getYaconfEnv('YII_DEBUG')); defined('YII_ENV') or define('YII_ENV', getYaconfEnv('YII_ENV')); defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', getYaconfEnv('YII_ENABLE_ERROR_HANDLER')); require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php'); // 暂时向下兼容老文件 require(__DIR__ . '/../../config/errorCode.php'); require(__DIR__ . '/../../config/constant.php'); Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend'); Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console'); Yii::setAlias('@psservice', dirname(dirname(__DIR__)) . '/psservice'); Yii::setAlias('@config', dirname(dirname(__DIR__)) . '/config'); $config = require(APP_PATH . '/../config/web.php');
Cest 代码示例
<?php namespace tests\api; use backend\models\user\EduOcrTourist; use backend\models\user\EduOcrUser; use backend\modules\user\services\UserEntity; use config\UserCacheKey; use psservice\models\user\WxEduUser; use tests\ApiTester; class UserControllerCest { private $token; public function _before(ApiTester $I) { } /** * 用户登录 * @param ApiTester $I */ public function actionLoginTest(ApiTester $I) { $mobile = '18510473853'; $token = $I->amUserLogin($mobile); $I->sendCommandToRedis('select', getPsYaconfEnv('PS_OCR_SINGLE_REDIS_DATABASE_1')); $userRedisInfo = $I->grabFromRedis(sprintf(UserCacheKey::USER_INFO, $token)); $userRedisInfo = json_decode($userRedisInfo, 1); codecept_debug($userRedisInfo); $I->assertGreaterThan(0, $userRedisInfo['userId']); $I->assertEquals(0, $userRedisInfo['touristUserId']); $I->assertEquals(UserEntity::$roleMap['user'], $userRedisInfo['role']); $I->assertEquals($mobile, $userRedisInfo['mobile']); $I->assertLessOrEquals(10, (time() - strtotime($userRedisInfo['loginTime']))); $I->seeRecord(WxEduUser::className(), ['UserId' => $userRedisInfo['commonUserId']]); } /** * 游客登录 * @param ApiTester $I */ public function actionTouristLoginTest(ApiTester $I) { $token = $I->amTouristLogin('6CB7B05E-DE62-415A-96CF-6D57B666F939'); $I->sendCommandToRedis('select', getPsYaconfEnv('PS_OCR_SINGLE_REDIS_DATABASE_1')); // $userRedisInfo = $I->grabFromRedis(sprintf(UserCacheKey::TOURIST_INFO, $token)); // 机缘巧合下,没有使用 touristInfo 常量 $userRedisInfo = $I->grabFromRedis(sprintf(UserCacheKey::USER_INFO, $token)); $userRedisInfo = json_decode($userRedisInfo, 1); codecept_debug($userRedisInfo); $I->assertGreaterThan(0, $userRedisInfo['id']); $I->seeRecord(EduOcrTourist::className(), ['deviceId' => '6CB7B05E-DE62-415A-96CF-6D57B666F939']); } /** * 登出 * @param ApiTester $I */ public function actionLogoutTest(ApiTester $I) { $this->token = $I->amUserLogin('18510473853'); $I->sendPOST('/user/user/logout', [ 'token' => $this->token, 'deviceId' => '6CB7B05E-DE62-415A-96CF-6D57B666F939', ]); $I->seeResponseContainsJson(['code' => 99999]); } /** * 登录用户信息 */ public function actionInfoTest(ApiTester $I) { $mobile = '18510473853'; $this->token = $I->amUserLogin($mobile); $I->sendGET('/user/user/info', [ 'token' => $this->token, ]); $ret = json_decode($I->grabResponse(), 1); $I->seeRecord(EduOcrUser::className(), ['id' => $ret['data']['userId'], 'mobile' => $mobile]); } }
八、增加自定义方法
tests/_support/Helper/{suite_name}.php
在其中增加方法后,可以通过 ApiTester 的对象 $I 进行调用。

九、运行测试代码生成测试报告(代码覆盖率)
php codecept.phar run -coverage --coverage-html --coverage-xml
测试报告会在 tests/_output/coverage/
中生成。
通过设定 nginx 静态服务器,可以查看测试报告。如:

十、利用测试用例进行重构
步骤:
- 编写测试代码
- 持续维护测试代码,提高测试代码覆盖率
- 运行测试代码使其通过。并不断重复 1、2、3 步骤
- 代码库越来越庞杂
- 重构
- 合并相似循环、方法、类
- 提取方法
- 等等
- 运行测试用例,找到失败的用例
- 修改重构后的代码,使其通过测试用例,并重复 5、6、7 步骤
十一、利用测试用例,快速了解新代码
steps
会在执行的时候打印出执行的断言,有助于观察测试用例做了什么,以及怎么使用你写的接口。
即测试用例是 : self-document 的。
php codecept.phar run api --steps # steps 会在执行的时候打印出执行的断言

本文来自博客园,作者:吴丹阳-V,转载请注明原文链接:https://www.cnblogs.com/wudanyang/p/10647875.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)