codeception测试使用框架模块Module

使用模块Module
使用框架模块

Codeception目前含有的模块有这些(最新的当前codeception版本:3.1.2):

 

具体想看当前版本安装的codecept.phar包含了哪些模块,可以通过PhpStorm等IDE工具看包里的源代码:

  模块定义文件在路径: codecept.phar > src > Module 目录下。

 想查询哪个模块的具体用法,有哪些具体参数,可以点击查看模块源文件。

比如查看Db模块信息:

 查看Db模块的配置有哪些参数,和哪些参数是必须的:

查看源码:

<?php
class Db extends CodeceptionModule implements DbInterface
{
    /**
     * @var array
     */
    protected $config = [
        'populate' => false,
        'cleanup' => false,
        'reconnect' => false,
        'waitlock' => 0,
        'dump' => null,
        'populator' => null,
    ];

    /**
     * @var array
     */
    protected $requiredFields = ['dsn', 'user', 'password'];

 $config 就是配置参数,$requiredFields 就是必须填写的参数。

数据库连接配置参考:

 * ## Example
 *
 *     modules:
 *        enabled:
 *           - Db:
 *              dsn: 'mysql:host=localhost;dbname=testdb'
 *              user: 'root'
 *              password: ''
 *              dump: 'tests/_data/dump.sql'
 *              populate: true
 *              cleanup: true
 *              reconnect: true
 *              waitlock: 10
 *              ssl_key: '/path/to/client-key.pem'
 *              ssl_cert: '/path/to/client-cert.pem'
 *              ssl_ca: '/path/to/ca-cert.pem'
 *              ssl_verify_server_cert: false
 *              ssl_cipher: 'AES256-SHA'
 *              initial_queries:
 *                  - 'CREATE DATABASE IF NOT EXISTS temp_db;'
 *                  - 'USE temp_db;'
 *                  - 'SET NAMES utf8;'
 *
 * ## Example with multi-dumps
 *     modules:
 *          enabled:
 *             - Db:
 *                dsn: 'mysql:host=localhost;dbname=testdb'
 *                user: 'root'
 *                password: ''
 *                dump:
 *                   - 'tests/_data/dump.sql'
 *                   - 'tests/_data/dump-2.sql'
 *
 * ## Example with multi-databases
 *
 *     modules:
 *        enabled:
 *           - Db:
 *              dsn: 'mysql:host=localhost;dbname=testdb'
 *              user: 'root'
 *              password: ''
 *              databases:
 *                 db2:
 *                    dsn: 'mysql:host=localhost;dbname=testdb2'
 *                    user: 'userdb2'
 *                    password: ''

连接到MySQL数据库:

* For MySQL:
 *
 * ```yaml
 * modules:
 *    enabled:
 *       - Db:
 *          dsn: 'mysql:host=localhost;dbname=testdb'
 *          user: 'root'
 *          password: ''
 *          dump: 'tests/_data/dump.sql'
 *          populate: true # run populator before all tests
 *          cleanup: true # run populator before each test
 *          populator: 'mysql -u $user -h $host $dbname < $dump'
 *          initial_queries:
 *              - 'CREATE DATABASE IF NOT EXISTS temp_db;'
 *              - 'USE temp_db;'
 *              - 'SET NAMES utf8;'

连接到PostgreSQL:

 * For PostgreSQL (using pg_restore)
 *
 * ```
 * modules:
 *    enabled:
 *       - Db:
 *          dsn: 'pgsql:host=localhost;dbname=testdb'
 *          user: 'root'
 *          password: ''
 *          dump: 'tests/_data/db_backup.dump'
 *          populate: true # run populator before all tests
 *          cleanup: true # run populator before each test
 *          populator: 'pg_restore -u $user -h $host -D $dbname < $dump'

 实践

配置连接数据库模块Db:

目前的unit.suite.xml配置文件:

actor: UnitTester
modules:
    enabled:
        - Asserts
        - \Helper\Unit
    step_decorators: ~

我们要增加几个模块,比如数据库模块Db,PhpBrowser, Filesystem等模块:

actor: UnitTester
modules:
    enabled:
        - Asserts
        - \Helper\Unit
        - Db:
              dsn: 'mysql:host=localhost;dbname=testdb'
              user: 'root'
              password: 'root'
              dump: 'tests/_data/dump.sql'
        - PhpBrowser:
              url: http://localhost
        - Filesystem
    step_decorators: ~

编写单元测试文件tests/unit/ExampleTest.php代码:

<?php 
class ExampleTest extends Codeception\Test\Unit
{
    /**
     * @var \UnitTester
     */
    protected $tester;
    
    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
        $this->assertTrue(true);

        $dbh = $this->getModule('Db')->_getDbh(); // dbh: contains the PDO connection 返回一个PDO连接对象
        // $dbh是一个PDO对象,就可以说使用任何PDO对象的方法 (https://www.php.net/manual/en/class.pdo.php)
        $sql = "SELECT username, email FROM `member` where username = 'lajox' ORDER BY id";
        // 比如使用PDO::query()方法
        foreach ($dbh->query($sql) as $row) {
            $this->assertContains('lajox', $row['username']); // $row['username']变量值是否包含字符串lajox
            $this->assertContains('blueno@yeah.net', $row['email']); // $row['email']变量值是否包含字符串blueno@yeah.net
        }

        $this->getModule('Db')->seeNumRecords(1, 'member', ['username' => 'lajox']); // 判断member表里username='lajox'的数据记录数是不是1(条)
        $this->getModule('Db')->seeInDatabase('member', ['username' => 'lajox', 'email like' => '%yeah.net%']); // 判断member表里username='lajox' AND email like '%yeah.net%'是否存在记录
        $this->getModule('Db')->dontSeeInDatabase('member', ['email like' => '%163.com%']); // 判断member表里email like '%163.com%'是否不存在记录
    }
}

Debug打印变量:

用内置的 codecept_debug() 方法来打印变量:

<?php 
class ExampleTest extends Codeception\Test\Unit
{
    /**
     * @var \UnitTester
     */
    protected $tester;
    
    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
        $this->assertTrue(true);

        $dbh = $this->getModule('Db')->_getDbh(); // dbh: contains the PDO connection 返回一个PDO连接对象
        // $dbh是一个PDO对象,就可以说使用任何PDO对象的方法 (https://www.php.net/manual/en/class.pdo.php)
        $sql = "SELECT username, email FROM `member` where username = 'lajox' ORDER BY id";
        // 比如使用PDO::query()方法
        foreach ($dbh->query($sql) as $row) {
            $this->assertContains('lajox', $row['username']); // $row['username']变量值是否包含字符串lajox
            $this->assertContains('blueno@yeah.net', $row['email']); // $row['email']变量值是否包含字符串blueno@yeah.net
        }

        $this->getModule('Db')->seeNumRecords(1, 'member', ['username' => 'lajox']); // 判断member表里username='lajox'的数据记录数是不是1(条)
        $this->getModule('Db')->seeInDatabase('member', ['username' => 'lajox', 'email like' => '%yeah.net%']); // 判断member表里username='lajox' AND email like '%yeah.net%'是否存在记录
        $this->getModule('Db')->dontSeeInDatabase('member', ['email like' => '%163.com%']); // 判断member表里email like '%163.com%'是否不存在记录

        $number = $this->getModule('Db')->grabNumRecords('member', ['username' => 'lajox']); // 获取member表里username='lajox'的记录数
        $mails = $this->getModule('Db')->grabColumnFromDatabase('member', 'email', array('username' => 'lajox')); // 获取member表里username='lajox'的邮箱字段email的数组列表
        //$email = $this->getModule('Db')->grabFromDatabase('member', 'email', ['email like' => '%blueno%']); // 获取member表里email like '%blueno%'的第一条数据的email字段值

        $this->debugSection('number', $number);
        $this->debugSection('MailList', $mails);
    }

    protected function debugSection($title, $message)
    {
        if (is_array($message) or is_object($message)) {
            $message = stripslashes(json_encode($message));
        }
        \codecept_debug("[$title] $message");
    }
}

命令行下,codecept run命令加上 --debug参数, 例如:

codecept run unit ExampleTest.php --steps --debug --fail-fast

 

 解释:命令行增加 --debug 参数才能完整详细输出打印信息:

又假如我们要验证测试一个点赞功能的正确性,可以这么做:

<?php 
class ExampleTest extends Codeception\Test\Unit
{
    /**
     * @var \UnitTester
     */
    protected $tester;
    
    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
        $testBookId = 99;    //要测试的书籍ID
        $bookSupport = $this->getModule('Db')->grabFromDatabase('book', 'support', ['id' => $testBookId]);    //从数据库取出书籍点赞记录

        //打印原始点赞数值
        $this->debugSection('bookSupport', $bookSupport);

        //假设这是对 ID为99的一本书进行"赞/喜欢"操作, 如果成功,结果应该是 喜欢数 support字段值 就会自动 + 1 了
        $this->getModule('PhpBrowser')->sendAjaxPostRequest('/book/support.do', ['id' => $testBookId]);

        $this->getModule('Db')->seeInDatabase('book', [
            'id' => $testBookId,
            'support' => $bookSupport + 1,
        ]);    //断言support字段是否被加1了

        $this->getModule('Db')->dontSeeInDatabase('book', [
            'id' => $testBookId,
            'support' => $bookSupport,
        ]); //或者断言不会是原来的值
    }

    protected function debugSection($title, $message)
    {
        if (is_array($message) or is_object($message)) {
            $message = stripslashes(json_encode($message));
        }
        \codecept_debug("[$title] $message");
    }
}

更多Db模块的方法用例,请查阅: https://codeception.com/docs/modules/Db

查阅文档:https://github.com/Codeception/codeception.github.com/blob/master/docs/modules/Db.md

 PhpStorm配置codeception单元测试运行参数:

编辑工具栏的Debug配置:

 弹出的Run/Debug Configurations窗口中,Test Runner Options 填入附加运行参数: --steps --debug

编辑配置文件 codeception.yml , 在默认开启的 extensions 可能只有 Codeception\Extension\RunFailed ,

需要增加几个 extensions 拓展选项开启,例如增加开启  Codeception\Extension\SimpleReporter,Codeception\Extension\DotReporter 等拓展:

include:
paths:
    tests: tests
    output: tests/_output
    data: tests/_data
    support: tests/_support
    envs: tests/_envs
actor_suffix: Tester
bootstrap: _bootstrap.php
settings:
    colors: true
    memory_limit: 1024M
extensions:
    enabled:
        - Codeception\Extension\RunBefore
        - Codeception\Extension\RunProcess
        - Codeception\Extension\RunFailed
        - Codeception\Extension\SimpleReporter
        - Codeception\Extension\DotReporter

 这样调试输出打印的运行日志信息会更多。

记住,修改yml配置文件之后,一般都要再运行 codecept build 构建代码:

最后点运行按钮 进行Debug单元测试:

 全方面的单元测试:

例如配置unit.suite.yml配置文件,增加几个模块:Db, PhpBrowser, REST等模块引入:

actor: UnitTester
bootstrap: _bootstrap.php
modules:
    enabled:
        - Asserts
        - \Helper\Unit
        - Db:
              dsn: 'mysql:host=localhost;dbname=testdb'
              user: 'root'
              password: 'root'
              dump: 'tests/_data/dump.sql'
        - PhpBrowser:
              url: http://www.lancms.com
        - REST:
            url: http://www.lancms.com
            depends: PhpBrowser
            part: Json
        - Filesystem
    step_decorators: ~

注释:bootstrap: _bootstrap.php 这个配置是引入了 _bootstrap.php 加载文件,可以往里面加你要引入的文件或类库。比如:

<?php
# 文件 tests/unit/_bootstrap.php
# 引入单元测试方法拓展基类
require_once __DIR__ . '/UnitBase.php';

新建一个 tests/unit/UnitBase.php 文件:

<?php
# 文件 tests/unit/UnitBase.php
class UnitBase extends Codeception\Test\Unit
{
    /**
     * 调用类的私有方法
     * @param object $object
     * @param string $className
     * @param string $methodName
     * @param array  $params
     * @return mixed
     */
    protected function callPrivateMethod($object, string $className, string $methodName, array $params) {
        $method = $this->getPrivateMethod($className, $methodName);
        return $method->invokeArgs($object, $params);
    }

    /**
     * 获取对象的私有属性
     * @param object $object
     * @param string $className
     * @param string $propertyName
     * @return mixed
     */
    protected function getPrivatePropertyValue($object, string $className, string $propertyName) {
        $property = $this->getPrivateProperty($className, $propertyName);
        return $property->getValue($object);
    }

    /**
     * getPrivateProperty
     *
     * @param string $className
     * @param string $propertyName
     * @return    ReflectionProperty
     * @author    Joe Sexton <joe@webtipblog.com>
     */
    protected function getPrivateProperty(string $className, string $propertyName): \ReflectionProperty {
        $reflector = new ReflectionClass($className);
        $property  = $reflector->getProperty($propertyName);
        $property->setAccessible(true);

        return $property;
    }

    /**
     * getPrivateMethod
     *
     * @param string $className
     * @param string $methodName
     * @return    ReflectionMethod
     * @author    Joe Sexton <joe@webtipblog.com>
     */
    protected function getPrivateMethod(string $className, string $methodName): \ReflectionMethod {
        $reflector = new ReflectionClass($className);
        $method    = $reflector->getMethod($methodName);
        $method->setAccessible(true);

        return $method;
    }
}

修改 tests/unit/ExampleTest.php,就可以把 继承方法 class ExampleTest extends Codeception\Test\Unit 改为: class ExampleTest extends UnitBase

编辑unit单元测试文件:tests/unit/ExampleTest.php

<?php
# 文件 tests/unit/ExampleTest.php
class ExampleTest extends Codeception\Test\Unit
{
    /**
     * @var \UnitTester
     */
    protected $tester;

    public function _before()
    {
    }

    public function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
        $this->assertTrue(true);

        $this->_testDB();          # 测试数据库模块: Db
        $this->_testPhpBrowser();  # 测试模块: PhpBrowser
        $this->_testREST();        # 测试api测试模块: REST
        $this->_testCept();        # 场景测试
        $this->_testScratch();     # 综合测试,混用
        $this->_testThinPHP();     # 测试ThinkPHP 5.1方法
    }

    protected function _testDB()
    {
        // 可以查看文档: https://codeception.com/docs/modules/Db
        $dbh = $this->getModule('Db')->_getDbh(); // dbh: contains the PDO connection 返回一个PDO连接对象
        // $dbh是一个PDO对象,就可以说使用任何PDO对象的方法 (https://www.php.net/manual/en/class.pdo.php)
        $sql = "SELECT username, email FROM `member` where username = 'lajox' ORDER BY id";
        // 比如使用PDO::query()方法
        foreach ($dbh->query($sql) as $row) {
            $this->assertContains('lajox', $row['username']); // $row['username']变量值是否包含字符串lajox
            $this->assertContains('blueno@yeah.net', $row['email']); // $row['email']变量值是否包含字符串blueno@yeah.net
        }

        $this->getModule('Db')->seeNumRecords(1, 'member', ['username' => 'lajox']); // 判断member表里username='lajox'的数据记录数是不是1(条)
        $this->getModule('Db')->seeInDatabase('member', ['username' => 'lajox', 'email like' => '%yeah.net%']); // 判断member表里username='lajox' AND email like '%yeah.net%'是否存在记录
        $this->getModule('Db')->dontSeeInDatabase('member', ['email like' => '%163.com%']); // 判断member表里email like '%163.com%'是否不存在记录

        $number = $this->getModule('Db')->grabNumRecords('member', ['username' => 'lajox']); // 获取member表里username='lajox'的记录数
        $mails = $this->getModule('Db')->grabColumnFromDatabase('member', 'email', array('username' => 'lajox')); // 获取member表里username='lajox'的邮箱字段email的数组列表
        $email = $this->getModule('Db')->grabFromDatabase('member', 'email', ['email like' => '%blueno%']); // 获取member表里email like '%blueno%'的数据列表(数组)

        $this->debugSection('number', $number);
        $this->debugSection('MailList', $mails);
        $this->debugSection('Email', $email);
    }

    protected function _testPhpBrowser()
    {
        // 可以查看文档: https://codeception.com/docs/modules/PhpBrowser
        $this->getModule('PhpBrowser')->haveHttpHeader('accept', 'application/json');
        // $this->getModule('PhpBrowser')->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded'); // 普通表单形式发送
        $this->getModule('PhpBrowser')->haveHttpHeader('content-type', 'application/json'); // 发送JSON形式数据
        // AJAX请求
        //$this->getModule('PhpBrowser')->sendAjaxPostRequest('/api/test/demo', ['name' => 'test', 'email' => 'test@163.com']);
        $this->getModule('PhpBrowser')->sendAjaxRequest('POST', 'http://www.lancms.com/api/test/demo', [
            'name' => 'test',
            'email' => 'test@163.com',
        ]);
        $this->getModule('PhpBrowser')->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
    }

    protected function _testREST()
    {
        # 发送请求api地址: http://www.lancms.com/api/test/demo
        # 请求参数: {"name":"test","email":"test@163.com"}
        # 响应结果: {"code":1,"msg":"success","data":[]}
        $this->getModule('REST')->haveHttpHeader('accept', 'application/json');
        // $I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded'); // 普通表单形式发送
        $this->getModule('REST')->haveHttpHeader('content-type', 'application/json'); // 发送JSON形式数据
        $this->getModule('REST')->sendPOST('/api/test/demo', [
            'name' => 'test',
            'email' => 'test@163.com',
        ]);
        $this->getModule('REST')->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
        $this->getModule('REST')->seeResponseCodeIs(200);
        $this->getModule('REST')->seeResponseIsJson();
        $this->getModule('REST')->seeResponseContains('success');
        $this->getModule('REST')->seeResponseContainsJson([
            "code" => "1",
        ]);
        $this->getModule('REST')->seeResponseJsonMatchesJsonPath("$.data");
        $this->getModule('REST')->seeResponseJsonMatchesXpath('//data');
        $this->getModule('REST')->seeResponseMatchesJsonType([
            'code' => 'integer',
            'msg' => 'string',
            'data' => 'string|array|null',
        ]);
    }

    /**
     * 测试场景
     * @var \UnitTester $this->tester
     */
    protected function _testCept()
    {
        $this->tester->amOnPage('/index/test/show');
        $this->tester->see('show_test');
    }

    /**
     * 综合测试,混用
     * @var \UnitTester $this->tester
     */
    protected function _testScratch()
    {
        # 因为unit.suite.yml已经配置了引入Db、PhpBrowser、REST等模块,
        # 所以 $this->tester引用对象就会自动包含各个模块的所有方法。可以用 $this->tester 简化替代 $this->getModule() 方法。

        # 模块Db方法:
        $this->getModule('Db')->seeNumRecords(1, 'member', ['username' => 'lajox']);
        $this->tester->seeNumRecords(1, 'member', ['username' => 'lajox']);

        # 模块PhpBrowser方法:
        $this->getModule('PhpBrowser')->sendAjaxPostRequest('/api/test/demo', ['name' => 'test', 'email' => 'test@163.com']);
        $this->tester->sendAjaxPostRequest('/api/test/demo', ['name' => 'test', 'email' => 'test@163.com']);

        # 模块REST方法:
        $this->getModule('REST')->sendPOST('/api/test/demo', ['name' => 'test', 'email' => 'test@163.com']);
        $this->tester->sendPOST('/api/test/demo', ['name' => 'test', 'email' => 'test@163.com']);
    }

    /**
     * 测试ThinkPHP的方法
     */
    protected function _testThinPHP()
    {
        $app = new \app\index\controller\Test();
        // 假设 index/test/show 方法返回的字符串中包含 "show_test"
        $this->assertContains('show_test', $app->show());
    }

    protected function debugSection($title, $message)
    {
        if (is_array($message) or is_object($message)) {
            $message = stripslashes(json_encode($message));
        }
        \codecept_debug("[$title] $message");
    }

}

phpstorm快速Debug测试unit单元测试方法:

 就能快速运行Debug单元测试了:

 编辑Debug工具栏, 配置此方法的选项:

 弹出的Run/Debug Configurations窗口中,Test Runner Options 填入附加运行参数: --steps --debug

 再次点击 运行按钮,就可以看到更多的Debug调试信息了:

 高级用法:

我们回顾一下,unit.suite.yml 配置文件默认是开启了 \Helper\Unit 模块:

 那么这个\Helper\Unit模块定义文件在哪呢?

我们看下tests/ 目录下的文件结构,就会发现:\Helper\Unit 模块定义文件在: tests/_support/Helper/Unit.php 文件中:

 修改 tests/_support/Helper/Unit.php 文件代码,我们增加了一个自定义方法 seeResponseIsValidate() :

<?php
namespace Helper;

// here you can define custom actions
// all public methods declared in helper class will be available in $I

class Unit extends \Codeception\Module
{
    public function seeResponseIsValidate()
    {
        $this->getModule('REST')->haveHttpHeader('accept', 'application/json');
        $this->getModule('REST')->haveHttpHeader('content-type', 'application/json'); // 发送JSON形式数据
        $this->getModule('REST')->sendPOST('/api/test/demo', [
            'name' => 'test',
            'email' => 'test@163.com',
        ]);
        $this->getModule('REST')->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
        $this->getModule('REST')->seeResponseCodeIs(200);
        $this->getModule('REST')->seeResponseIsJson();
        $this->getModule('REST')->seeResponseContains('success');
        $response = $this->getModule('REST')->grabResponse();
        $this->assertContains('success', $response);
    }
}

修改文件:文件 tests/unit/ExampleTest.php,内容为:

<?php
# 文件 tests/unit/ExampleTest.php
class ExampleTest extends UnitBase
{
    /**
     * @var \UnitTester
     */
    protected $tester;

    public function _before()
    {
    }

    public function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
        $this->assertTrue(true);

        $this->_testHelperUnit();          # 测试业务代码: tests/_support/Helper/Unit.php
    }

    /**
     * 测试业务代码放入 tests/_support/Helper/Unit.php 文件中
     */
    protected function _testHelperUnit()
    {
        $this->getModule('\Helper\Unit')->seeResponseIsValidate();
    }

    protected function debugSection($title, $message)
    {
        if (is_array($message) or is_object($message)) {
            $message = stripslashes(json_encode($message));
        }
        \codecept_debug("[$title] $message");
    }

}

运行一下Debug按钮:

 添加新模块文件:

通过命令 codecept generate:helper "Unit\MyModule" 就会自动在 tests/_support/Helper/ 目录下生成一个文件夹Unit和Unit文件夹下的文件 MyModule.php:

当然,你也可以手动新建目录Unit和文件 Unit/MyModule.php ,如果你不嫌麻烦的话。

codecept generate:helper "Unit\MyModule"

 编辑文件 tests/_support/Helper/Unit/MyModule.php ,代码写入:

<?php
# 文件 tests/_support/Helper/Unit/MyModule.php
namespace Helper\Unit;

// here you can define custom actions
// all public methods declared in helper class will be available in $I

class MyModule extends \Codeception\Module
{
    public function seeMyModuleIsValidate()
    {
        $this->assertTrue(false);
    }
}

配置 unit.suite.yml 配置文件,加入引入模块代码:\Helper\Unit\MyModule

# Codeception Test Suite Configuration
#
# Suite for unit or integration tests.

actor: UnitTester
bootstrap: _bootstrap.php
modules:
    enabled:
        - Asserts
        - \Helper\Unit
        - \Helper\Unit\MyModule
        - Db:
              dsn: 'mysql:host=localhost;dbname=testdb'
              user: 'root'
              password: 'root'
              dump: 'tests/_data/dump.sql'
        - PhpBrowser:
              url: http://www.lancms.com
        - REST:
            url: http://www.lancms.com
            depends: PhpBrowser
            part: Json
        - Filesystem
    step_decorators: ~

然后命令行执行命令 codecept build  构建代码。

  看到  includes modules: 和 \Helper\Unit\MyModule 提示文字表示新的模块已经引用进来:

 

编辑 tests/_support/Helper/Unit.php ,代码:

<?php
# 文件 tests/_support/Helper/Unit.php
namespace Helper;

// here you can define custom actions
// all public methods declared in helper class will be available in $I

class Unit extends \Codeception\Module
{
    public function seeResponseIsValidate()
    {
        $this->getModule('REST')->haveHttpHeader('accept', 'application/json');
        $this->getModule('REST')->haveHttpHeader('content-type', 'application/json'); // 发送JSON形式数据
        $this->getModule('REST')->sendPOST('/api/test/demo', [
            'name' => 'test',
            'email' => 'test@163.com',
        ]);
        $this->getModule('REST')->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
        $this->getModule('REST')->seeResponseCodeIs(200);
        $this->getModule('REST')->seeResponseIsJson();
        $this->getModule('REST')->seeResponseContains('success');
        $response = $this->getModule('REST')->grabResponse();
        $this->assertContains('success', $response);
    }

    public function seeMyModule()
    {
        $this->getModule('\Helper\Unit\MyModule')->seeMyModuleIsValidate();
    }
}

编辑文件:tests/unit/ExampleTest.php ,代码:

<?php
# 文件 tests/unit/ExampleTest.php
class ExampleTest extends UnitBase
{
    /**
     * @var \UnitTester
     */
    protected $tester;

    public function _before()
    {
    }

    public function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
        $this->assertTrue(true);

        $this->_testHelperUnit();          # 测试业务代码: tests/_support/Helper/Unit.php
    }

    /**
     * 测试业务代码放入 tests/_support/Helper/Unit.php 文件中
     */
    protected function _testHelperUnit()
    {
        $this->getModule('\Helper\Unit')->seeResponseIsValidate();
        $this->getModule('\Helper\Unit')->seeMyModule();
        $this->getModule('\Helper\Unit\MyModule')->seeMyModuleIsValidate();

        $this->_testCept(); // 测试场景

        # debug输出一条信息
        $this->debugSection('CurrentMethod', __METHOD__); // 就会输出: [CurrentMethod] ExampleTest::_testHelperUnit
    }

    /**
     * 测试场景
     * @var \UnitTester $this->tester
     */
    protected function _testCept()
    {
        $this->tester->amOnPage('/index/test/show');
        $this->tester->see('show_test');
        # 可以将场景测试代码移入文件 tests/_support/UnitTester.php 中
        $this->tester->seeUnitTester();
    }

    protected function debugSection($title, $message)
    {
        if (is_array($message) or is_object($message)) {
            $message = stripslashes(json_encode($message));
        }
        \codecept_debug("[$title] $message");
    }

}

编辑文件 tests/_support/UnitTester.php, 代码:

<?php
# 文件 tests/_support/UnitTester.php

/**
 * Inherited Methods
 * @method void wantToTest($text)
 * @method void wantTo($text)
 * @method void execute($callable)
 * @method void expectTo($prediction)
 * @method void expect($prediction)
 * @method void amGoingTo($argumentation)
 * @method void am($role)
 * @method void lookForwardTo($achieveValue)
 * @method void comment($description)
 * @method void pause()
 *
 * @SuppressWarnings(PHPMD)
*/
class UnitTester extends \Codeception\Actor
{
    use _generated\UnitTesterActions;

   /**
    * Define custom actions here
    */
    public function seeUnitTester()
    {
        $this->amOnPage('/index/test/show');
        $this->see('show_test');

        # 因为unit.suite.yml已经配置了引入Db、PhpBrowser、REST等模块,
        # 所以 $this引用对象就会自动包含各个模块的所有方法

        # 使用Db模块方法, 可以查看文档: https://codeception.com/docs/modules/Db
        $this->seeNumRecords(1, 'member', ['username' => 'lajox']);

        # 使用PhpBrowser模块方法, 可以查看文档: https://codeception.com/docs/modules/PhpBrowser
        $this->sendAjaxRequest('POST', '/api/test/demo', [
            'name' => 'test',
            'email' => 'test@163.com',
        ]);

        # 使用REST模块方法, 可以查看文档: https://codeception.com/docs/modules/REST
        $this->sendPOST('/api/test/demo', [
            'name' => 'test',
            'email' => 'test@163.com',
        ]);

        # debug输出一条信息
        $this->debugSection('CurrentMethod', __METHOD__); // 就会输出: [CurrentMethod] UnitTester::seeUnitTester
    }

    /**
     * debug
     */
    protected function debugSection($title, $message)
    {
        if (is_array($message) or is_object($message)) {
            $message = stripslashes(json_encode($message));
        }
        \codecept_debug("[$title] $message");
    }
}

点击运行一下Debug按钮,查看测试运行效果:

 创建更多的单元测试文件,以文件夹Common, Controller 等为归组:

codecept generate:test unit "Common\Common"
codecept generate:test unit "Controller\Controller"

注:

codecept generate:test unit "Common\Common" 命令会在 tests/unit/ 目录下生成 Common/CommonTest.php (文件名以Test.php为后缀)

codecept generate:test unit "Common\Controller" 命令会在 tests/unit/ 目录下生成 Controller/ControllerTest.php (文件名以Test.php为后缀)

文件 tests/unit/Common/CommonTest.php 的生成默认代码:

<?php
# 文件 tests/unit/Common/CommonTest.php
namespace Common;

class CommonTest extends \Codeception\Test\Unit
{
    /**
     * @var \UnitTester
     */
    protected $tester;
    
    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {

    }
}

phpstorm配置只运行unit类型的单元测试: 

 

 

 

 

注: unit单元测试文件的类可以继承 Codeception\Test\Unit ,也可以写成继承 Codeception\TestCase\Test

<?php 
# 文件 tests/unit/ExampleTest.php
class ExampleTest extends Codeception\TestCase\Test
{
    /**
     * @var \UnitTester
     */
    protected $tester;
    
    public function _before()
    {
    }

    public function _after()
    {
    }

    // tests
    public function testSomeFeature()
    {
    }
}

 

上面单元测试实例源码已经放到Github上了: https://github.com/lajox/thinkphp-codeception-example

模块参考资料:
https://codeception.com/docs/06-ModulesAndHelpers

https://github.com/Codeception/codeception.github.com

http://www.kkh86.com/it/codeception/guide-cept-test-base.html
https://www.cloudxns.net/Support/detail/id/1005.html

 

posted @ 2019-12-21 23:25  php学习笔记  阅读(892)  评论(0编辑  收藏  举报