博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Yii2单元测试初探
阅读量:6186 次
发布时间:2019-06-21

本文共 5876 字,大约阅读时间需要 19 分钟。

tests目录结构解析,怎么这么多yml和_bootstrap?codeception运行流程,build干了什么?run干了什么?codeception.yml怎样发挥作用?modules如何被加载?$tester->haveFixtures()方法是哪里来的?

1.环境

Yii2 advanced模板,version:2.0.11.2,已安装codeception扩展,yii2-codeception扩展

2.tests目录结构

这一版本的tests目录结构不同于后来的更高版本,但总体思想理解后便于版本升级后的快速理解。

tests--codeception----backend--------unit                  backend单元测试文件目录------------_bootstrap.php    backend单元测试所需变量定义,执行run->Codecept::run()->runSuite()->SuitManager::initialize()触发SUITE_INIT事件,此时加载这里的_bootstrap文件,注意:此处文件名称应与yml文件中指定的settings:bootstrap一致,否则抛异常------------TestCase.php      继承自yii\codeception\TestCase,指定了配置文件为'@tests/codeception/config/backend/unit.php'------------DbTestCase.php    继承自yii\codeception\DbTestCase,指定了配置文件为'@tests/codeception/config/backend/unit.php'--------acceptance            backend验收测试文件目录--------functional            backend功能测试文件目录--------_bootstrap.php        执行build命令时若codeception.yml文件指定了settings:bootstrap则在此时加载其内容,参见Codeception\Configuration::config()--------codeception.yml       backend所有测试的测试配置信息--------unit.suite.yml        backend所有单元测试套件的测试配置信息--------acceptance.suite.yml  backend所有验收测试套件的测试配置信息--------functional.suite.yml  backend所有功能测试套件的测试配置信息----config--------backend------------unit.php          backend单元测试指定的配置文件------------acceptance.php    backend验收测试指定的配置文件------------functional.php    backend功能测试指定的配置文件------------config.php        backend所有测试均需设置的配置信息--------frontend------------配置结构同backend--------acceptance.php        面向所有验收测试的配置信息--------functional.php        面向所有功能测试的配置信息--------unit.php              面向所有单元测试的配置信息--------config.php            面向所有测试的公共配置信息--codeception.yml  执行所有测试的测试配置信息

3.运行单元测试

(1)cd 进入项目根目录;

(2)执行build命令,-c指定测试配置文件

php codeception/codeception/codecept build -c /tests/codeception/backend/codeception.yml

(3)指定测试yml路径,指定执行某个单元测试

php codeception/codeception/codecept run -c /tests/codeception/backend/codeception.yml -- unit resource/MyTest.php

窥探整体运行流程

(1)入口:codeception/codeception/Codecept,文件路径vendor/codeception/codeception/codecept,内容其实是php代码,实例化一个Codeception\Application对象$app,并添加了预设的命令,例如:build,run,GenerateCept等,$app->run();

(2)上面$app->run()获得命令行输入的参数后执行基类Symfony\Component\Console\Application->run() =>doRun()=>doRunCommand();

这一步从参数获得要执行的命令,接下来执行命令。

(3)codeception的所有命令都在vendor/codeception/src/Codeception/Command目录中,均继承自Symfony\Component\Console\Command\Command。以build命令为例:

Symfony\Component\Console\Command\Command::run()=>Codeception\Command\Build::execute()

这里开始了build命令的真正执行,找到了命令的入口,接下来就看看常用的build和run命令具体干了些什么,那些配置文件是在何时发挥作用的。

4.build干了什么?

(1)命令入口:Codeception\Command\Build::execute()=>$this->buildActorsForConfig()

(2)加载全局测试配置信息: Codeception\Command\Shared\Config::getGlobalConfig() => Codeception\Configuration::config()

这里执行了一个trait的方法用于加载全局测试配置信息,也就是-c参数指定的codeception.yml文件的内容,过程中会调用Codeception\Configuration::loadBootstrap()此时加载指定测试目录下的bootstrap文件,通常是_bootstrap.php,在3小节中会加载tests/backend/_bootstrap.php文件内容。

(3)构建测试套件:$this->buildSuiteActors(),这里主要看两步骤内容:

1)构建测试方法:在_support/_generated目录下生成与suite的class_name对应的trait文件,内容是在yml中配置的module的所有可以在测试文件中使用的actions

=>$this->buildActions()=>Codeception\Lib\Generator\Actions::produce()

2)构建测试角色:在_support目录下生成与suite的class_name对应的文件,内容是一个actor类,引用了上一步生成的对应suite的action trait

=>$this->buildActor()=>Codeception\Lib\Generator\Actor::produce()

到这里,找到了codeception.yml的加载时机,知道了_support里的文件是怎么来的,以及yml中指定的modules内容是如何可以在测试文件中通过actor对象直接访问的,终于知道模板示例中的$tester->haveFixture()方法是怎么来的了。

5.run命令干了什么?

(1)命令入口:Codeception\Command\Run::execute();=> new Codeception\Codecept();

(2)准备运行测试套件: Codeception\Codecept::run($suite,$test); => Codeception\Codecept::runSuite();

(3)由套件管理器运行测试套件: $suiteManager = new Codeception\SuiteManager(); $suiteManager->initialize()

这里的套件管理器初始化时会触发Events::MODULE_INIT事件,从而执行yml文件指定的modules的_initialize()方法,Events::SUITE_INIT事件,这一事件的订阅者会加载对应suite下的bootstrap文件,在3小节示例中对应的是tests/backend/unit/_bootstrap.php文件。

(4)运行测试:PHPUnit\Runner::doEnhanceRun();

=> yii\codeception\TestCase::run(); => \PHPUnit_Framework_TestCase::run(); => \PHPUnit_Framework_TestResult::run(); startTest(); endTest();

到这里,找到了各个套件下_bootstrap.php文件的加载时机,也找到了modules的初始化时机,这里提到了codeception的事件处理,下面总结下codeception的事件发布订阅机制。

6.dispatcher和subscriber

发布者:Symfony\Component\EventDispatcher\EventDispatcher 实现了EventDispatcherInterface

订阅者:codeception实现的订阅者在目录vendor/codeception/codeception/src/Codeception/Subscriber

第5小节提到的两个订阅者分别是Bootstrap和Module,订阅者都有一个静态成员$events记录着各自订阅的事件与对应的处理方法。

7.modules是怎样运作的

codeception的modules存在于目录:vendor/codeception/codeception/src/Codeception/Module

在build命令执行过程中需要加载测试依赖的modules并实例化,modules之间可以存在依赖关系,这里离不开依赖注入的实现Codeception\Lib\Di类。

 build命令执行到Codeception\Lib\Generator\Actions::produce()前,在Codeception\Lib\Generator\Actions::__construct()这一步实例化Di,ModuleContainer,获取所需modules;

(1)获取yml配置的modules名称列表: Codeception\Configuration::modules()
(2)使用Di实例化modules并获取可用actions:Codeception\Lib\ModuleContainer::__construct(Di $di,$config) =>Codeception\Lib\ModuleContainer::create()

到这里知道了yml中配置的modules是如何被找到并实例化,以及依赖关系是怎样处理的,那一个具体的module是如何在测试文件中发挥作用的?

以Yii2这个module为例,这里有一些钩子函数,_initialize(),_before()等,分别在测试的不同环节被执行,就像run命令运行到套件管理器初始化时会触发事件导致module的_initialize()方法得以执行。_before()方法在Events::TEST_BEFORE事件触发时运行,并会根据$configFile指定的配置文件实例化一个Yii::$app对象供测试方法使用。

8.$this->tester属性是在哪里定义?何时实例化的?

猜想应该是在Codeception\Test\Unit基类里,绝不会跑到PhpUnit层。上面已经知道Actor是在build命令中构建的,这里的$tester就是Actor的实例。果然在Codeception\Test\Unit类的setUp()方法中找到了该属性的注入代码。

protected function setUp()    {       //......此处省略部分代码        /** @var $di Di  **/        $di = $this->getMetadata()->getService('di');        $di->set(new Scenario($this));        // auto-inject $tester property        if (($this->getMetadata()->getCurrent('actor')) && ($property = lcfirst(Configuration::config()['actor_suffix']))) {            $this->$property = $di->instantiate($this->getMetadata()->getCurrent('actor'));        }        //......此处省略部分代码    }

转载于:https://www.cnblogs.com/ling-diary/p/9110906.html

你可能感兴趣的文章
小程序·云开发——正在悄悄改变小程序开发的模式
查看>>
运行期间抛出NoSuchMethodError模拟及原因分析
查看>>
基于Spring Boot2 + Spring Security OAuth2 实现单点登陆(一)
查看>>
跟我一起来用C++写web服务器吧(二)
查看>>
获取图片的旋转角度信息
查看>>
句柄泄漏和Handler的底层机制
查看>>
Refresh Token的使用场景以及如何与JWT交互
查看>>
聊聊jvm的CompressedClassSpace
查看>>
未来几年,BCH超越BTC的路径是什么?
查看>>
import和require的区别
查看>>
一个离开学校三年java架构师
查看>>
页面优化小总结 (图片类型)
查看>>
mysql中sum()与if()联合使用
查看>>
vue-resource安装与应用
查看>>
React编程规范
查看>>
iOS KVC与KVO
查看>>
秋招总结:一篇文章搞定秋招学习规划
查看>>
antd Form组件方法getFieldsValue获取自定义组件的值
查看>>
python爬虫系列(3.2-lxml库的使用)
查看>>
SEO提高网站排名快速见效的方法
查看>>