【发布时间】:2015-07-20 21:09:53
【问题描述】:
我在代码覆盖率方面遇到问题。
我正在使用 ZF2 2.4,其中包含由 composer 安装的十几个模块。 当我在没有代码覆盖的情况下运行测试时,5 分钟内就会有结果。在 Jenkins 中,完整构建需要 15 分钟。
启用代码覆盖后,整个构建时间为凌晨 0:00 到 5:30。
检查结果,我意识到服务/存储库/控制器类的测试平均每个需要 2.10 分钟。有什么可怕的。
我是 tdd 开发的新手,可以达到 100% 的代码覆盖率,系统有 3 个模块和 10 个实体。
我发现唯一的问题是我已经掌握了创建mock,然后无法mock所有应用服务,例如Repository/Service/Controller实时添加、读取、更新和删除测试数据库。
由于我的时间很短,我的主要问题是问题是否真的是不正确的模拟,因为如果代码覆盖率使用读取哪些文件夹的元信息,当你开始进入供应商时应该会感到震惊文件夹、Doctrine 等
所以这可能是主要问题?还没有模拟银行的联系?
根据我目前的技术知识,mock 会产生很大的不同,但是直到今天我在这里工作的地方还没有人使用 mock,并且我正在尝试彻底改变。
该原理已经应用了互联网帖子中描述的所有技术以及stackoverflow中的其他问题,是否有任何新技术可以加快代码覆盖率?
有一本手册重点介绍了如何加快 PHP 的代码覆盖率?完整的解释?
感谢收听。
编辑:
Codeception 套件模块配置:
namespace: ColumnNotNull
actor: Tester
paths:
tests: test
log: build/coverage
data: test/_data
helpers: test/_support
settings:
strict_xml: true
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
coverage:
whitelist:
include:
- src/*
exclude:
- src/ColumnNotNull/Fixture/*
- src/ColumnNotNull/Module.php*
blacklist:
include:
- build/*
- data/*
- language/*
- public/*
- schema/*
- test/*
- view/*
- src/ColumnNotNull/Fixture/*
Codeception Unit 套件模块配置:
class_name: UnitTester
modules:
enabled: [Asserts, UnitHelper, Db]
config:
Db:
dsn: 'mysql:dbname=pibernews;host=localhost'
user: 'piber'
password: 'secret'
dump: data/column-not-null.mysql.sql
coverage:
enabled: true
remote_enable : false
Codeception 项目套件
include:
- module/Column
- module/ColumnImage
- module/ColumnNotNull
paths:
log: build
settings:
colors: true
使用 codeception,我可以运行来自不同模块的多个测试并获得合并结果。
是一个项目,包含三个模块。通过验收和功能测试。验收 + 功能运行需要 15 分钟,代码覆盖单元需要 5 小时。
没有代码覆盖需要 10 分钟来运行所有单元测试。这是可以接受的。但我想缩短代码覆盖时间,因为五个小时是不可接受的。
编辑 2:
<?php
// This is global bootstrap for autoloading
include 'unit/GearBaseTest/ZendServiceLocator.php';
$zendServiceLocator = new \GearBaseTest\ZendServiceLocator();
_bootstrap.php 文件
<?php
namespace GearBaseTest;
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class ZendServiceLocator
{
public function __construct()
{
$this->chroot();
$zf2ModulePaths = array(
dirname(dirname(realpath(__DIR__ . '/../../')))
);
if (($path = $this->findParentPath('vendor'))) {
$zf2ModulePaths[] = $path;
}
if (($path = $this->findParentPath('module')) !== $zf2ModulePaths[0]) {
$zf2ModulePaths[] = $path;
}
$this->initAutoloader();
$env = getenv('APP_ENV') ? : 'testing';
$applicationConfig = include \GearBase\Module::getProjectFolder().'/config/application.config.php';
$config = array(
'module_listener_options' => array(
'module_paths' => $zf2ModulePaths,
'config_glob_paths' => array(
sprintf('config/autoload/{,*.}{global,%s,local}.php', $env)
)
),
'modules' => $applicationConfig['modules']
);
$serviceLocator = new ServiceManager(new ServiceManagerConfig());
$serviceLocator->setService('ApplicationConfig', $config);
$serviceLocator->get('ModuleManager')->loadModules();
$this->serviceLocator = $serviceLocator;
}
public function getServiceManager()
{
return $this->getServiceLocator()->get('ServiceManager');
}
public function chroot()
{
$rootPath = dirname($this->findParentPath('module'));
chdir($rootPath);
}
protected function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (! is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;
}
return $dir . '/' . $path;
}
public function getEntityManager()
{
if (!isset($this->entityManager)) {
$this->entityManager = $this->getServiceLocator()
->get('doctrine.entitymanager.orm_default');
}
return $this->entityManager;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
if (!isset($this->serviceLocator)) {
$this->serviceLocator = $serviceLocator;
}
return $this->serviceLocator;
}
protected function initAutoloader()
{
$vendorPath = $this->findParentPath('vendor');
$zf2Path = getenv('ZF2_PATH');
if (! $zf2Path) {
if (defined('ZF2_PATH')) {
$zf2Path = ZF2_PATH;
} elseif (is_dir($vendorPath . '/ZF2/library')) {
$zf2Path = $vendorPath . '/ZF2/library';
} elseif (is_dir($vendorPath . '/zendframework/zendframework/library')) {
$zf2Path = $vendorPath . '/zendframework/zendframework/library';
}
}
if (! $zf2Path) {
throw new RuntimeException(
'Unable to load ZF2. Run `php composer.phar install` or' . ' define a ZF2_PATH environment variable.'
);
}
if (file_exists($vendorPath . '/autoload.php')) {
include $vendorPath . '/autoload.php';
}
include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__
)
)
));
}
}
ZendServiceLocator.php 抓取器。
【问题讨论】:
-
你的问题不是很清楚。如果您正在对所有内容(包括供应商文件夹的内容)运行代码覆盖,我预计这将花费非常长的时间。否则,也许您可以多解释一下您的测试是如何设置的。恕我直言,您希望谨慎使用模拟,并且您的大多数测试都不需要接触数据库。
-
我意识到我在表达这个问题上有困难,谢谢你帮助我。所以我的测试应该只测试模块中的文件,但是当存储库在表中插入一行时,会调用额外的文件示例“Doctrine \ Orm \ EntityManager”,因为我没有使用 Mock 的持久化和刷新。代码覆盖设置仅限于我需要的文件夹,我的主要问题是:单元测试使用的所有类也用于代码覆盖,达到最大深度,导致开销信息并导致过度延迟?
-
正如我所说,我的同事中没有人使用代码覆盖率,而我是我使用的业务中的第一个,所以我知道通过使用受限的代码覆盖率设置就足以不用计算供应商文件夹。为最关键的测试创建模拟的成本很高,例如确保这是导致延迟的主要原因。我为 PHPUnit + 代码覆盖工作设置的设置非常基本,但如有必要我会忽略它们,哪些设置可能会在时间代码覆盖方面出现问题?那我就发到这里,大家可以一一查看。
-
如果 phpunit 配置文件不太大,能否将其添加到您的问题中?
标签: php unit-testing jenkins zend-framework2 code-coverage