zoukankan      html  css  js  c++  java
  • 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 提供了两种写测试代码的方式

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

    七、与Yii2集成

    配置:

    image-20190305200555502.png

    _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 testsapi;
    
    use backendmodelsuserEduOcrTourist;
    use backendmodelsuserEduOcrUser;
    use backendmodulesuserservicesUserEntity;
    use configUserCacheKey;
    use psservicemodelsuserWxEduUser;
    use testsApiTester;
    
    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 进行调用。

    image-20190305201253542.png

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

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

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

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

    image-20190305204312240.png

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

    步骤:

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

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

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

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

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

    image-20190305202846264.png

  • 相关阅读:
    HDU 3681 Prison Break 越狱(状压DP,变形)
    POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)
    ZOJ 3471 Most Powerful (状压DP,经典)
    POJ 2288 Islands and Bridges (状压DP,变形)
    HDU 3001 Travelling (状压DP,3进制)
    POJ 3311 Hie with the Pie (状压DP)
    POJ 1185 炮兵阵地 (状压DP,轮廓线DP)
    FZU 2204 7
    POJ 3254 Corn Fields (状压DP,轮廓线DP)
    ZOJ 3494 BCD Code (数位DP,AC自动机)
  • 原文地址:https://www.cnblogs.com/wudanyang/p/10647875.html
Copyright © 2011-2022 走看看