superreal/srunit

包含专门用于编写OXID模块单元测试环境的库。

v0.9.1 2014-10-10 12:45 UTC

This package is not auto-updated.

Last update: 2024-09-14 15:47:28 UTC


README

本包包含编写oxid模块单元测试所需的所有必要组件 - 包括启动和模拟功能。

安装

只需将以下要求添加到您的项目composer.json文件中,然后调用composer update superreal/srunit

"superreal/srunit": "0.10.*@dev"

所有必需的包将自动安装(例如PHPUnit,Mockery)。

为模块设置单元测试

以下步骤是设置您的模块单元测试所需的。

模块配置

将phpunit.xml添加到模块根目录,至少包含以下内容

<phpunit bootstrap="tests/bootstrap.php">
  <testsuites>
    <testsuite name="Module Tests">
        <directory>tests</directory>
    </testsuite>
  </testsuites>
  <listeners>
    <listener class='SrUnit\Adapter\Phpunit\TestListener' />
  </listeners>
</phpunit>

项目/商店配置

将phpunit.xml添加到商店根目录,包含以下内容

<phpunit bootstrap="tests/bootstrap.php">
  <testsuites>
    <testsuite name="Project Tests">
        <directory>tests</directory>
    </testsuite>
    <testsuite name="Module Tests">
        <directory>modules</directory>
    </testsuite>     
  </testsuites>
  <listeners>
    <listener class='SrUnit\Adapter\Phpunit\TestListener' />
  </listeners>
</phpunit>

完成上述步骤后,您可以从商店根目录运行phpunit,所有测试都将执行(项目和相关模块)。

注意:添加TestListener的效果是,在每个测试后都会验证预期。

启动

您的测试应放置在tests目录下。在tests目录下放置您的bootstrap.php,内容如下

\SrUnit\Bootstrap::create(__DIR__)->bootstrap();

启动过程将自动检索所有必需的目录,并加载composer自动加载器,以及基于您的metadata.php配置的OXID模块类的自定义自动加载器

这也适用于您从商店根目录运行测试的情况。在这种情况下,启动过程将设置所有测试的自动加载,包括模块内的测试。但这是基于您模块的正确配置。这意味着:模块负责正确设置自动加载。

这首先是通过在您的composer.json中添加“autoload”配置来完成的。如果您需要为Namespace定义多个目录(例如,为测试),您必须在那里做。除了这些,metadata.php也被考虑在内。在"extend"和/或"files"部分。

测试用例必须继承自SrUnit\TestCase

您所有的测试用例都应该继承自SrUnit\TestCase,以便启用OXID相关功能或方便方法。

加载OXID

OXID默认不加载。基本功能,如oxNew()oxDb::getDb()被模拟。您可以通过使用模拟来控制它们的行为。

如果您需要加载OXID(例如,进行集成测试),您可以通过添加组注解来加载OXID

@group needs-oxid

TestListener将激活特定测试的OXID加载,并启用/禁用所需的模块superreal/srunit-module。该模块也必须在您的composer.json中要求 - 否则测试将因异常而终止。

运行phpunit

当您设置好环境,如前所述,您可以在商店根目录或特定模块中运行phpunit。但为了正确设置自动加载,您需要运行随composer提供的phpunit。根据您的设置,您可以使用以下调用

bin/phpunit
vendor/bin/phpunit

使用Mock-Factory

Factory支持您创建模拟来替换SUT的依赖项。它还通过易于理解的流畅接口处理OXID相关的要求。

当您在方法链的末尾调用getMock()方法时,您将获得带有少量附加功能的Mockery\MockInterface(例如,实现ArrayAccess())。

底层库是Mockery,即使它没有被直接调用,你也会从Mockery中得到Mock对象。

创建简单Mock

简单Mock,简单调用

$mock = Factory::create('TestClass')->getMock();

之后,你可以通过简单地使用Mockery方法来定义Mock的行为

$mock->shouldReceive('getParam')->andReturn('a-value')

测试OXID扩展

当涉及到扩展OXID核心类(例如oxArticle)时,你可能需要测试你的实现是否正确。如果你不需要整个OXID堆栈来测试你的实现,你可以通过这样做来Mock仅仅是_parent

$mock = Factory::createParentClass('\SrMyExtensionOxArticle_parent')->getMock();

请注意,这个调用实际上会定义一个名为SrMyExtensionOxArticle_parent的类,并应用你将在其上应用的行为。

这意味着:在初始实例化之后,该类在整个PHP过程中将具有相同的行为。每次你创建一个新的实例,你都会得到相同的结果。当你需要为不同的测试提供不同的行为时,你必须通过添加以下注解来独立运行你的测试

/**
 * @runInSeparateProcess
 */
public function testInSeparateProcess()
{
    // ...
}

使用OXID-Factory的集成测试

如果你需要测试你模块的集成,或者你想使用OXID工厂以便拥有整个堆栈可用,你可以使用以下调用

$mock = Factory::create('\oxArticle')
    ->registerForOxNew()
    ->getMock();

这个调用将创建一个Mock,并将这个Mock对象注册到每次调用oxNew('oxArticle')时可以检索。当你有依赖于经常使用oxNew()调用的类时,这非常有用,并且你无法从外部更改此行为。

提供的Mock

通常,你不想创建Mock,并且反复应用相同的行为。对于这种情况,你可以使用提供程序来获取具有默认值/存根的Mock。

$mock = Factory::create('\oxArticle')
    ->useProvisioning()
    ->getMock();

在某些情况下,这个调用将导致异常,因为没有提供程序可用。然后你需要自己实现一个提供程序。

具有接口的Mock

你可以定义Mock应该实现的接口,如下所示

$mock = Factory::create('TestClass')
    ->implementsInterface('\Iterator')
    ->implementsInterface('\Mockable')
    ->getMock();

请注意:接口必须存在!

对于某些Iterator接口,已经启用了特定方法的存根机制

  • Iterator
  • ArrayAccess

对于这些接口,需要向方法传递数据,才能获得期望的行为

$data = array('foo', 'bar', 'barz');

$mock = Factory::create('TestClass')
    ->implementsInterface('\Iterator', $data)
    ->getMock();

当你使用这个Mock时,你会遍历给定数据。

Mock文件系统

如果你的系统依赖文件系统,并且你想设置特定的测试环境,你可以使用文件系统实用程序。

创建文件系统

你可以选择使用虚拟文件系统或物理文件系统。虽然虚拟文件系统适用于大多数情况,但有时需要使用物理文件系统(例如,如果你正在处理符号链接)。

(虚拟文件系统是通过vfsStream实现的)

$fs = new VirtualFilesystem($rootDir);

$fs = new Filesystem($rootDir); 

通过使用TestCase::createFilesystem()

在你的TestCase中,你可以调用createFilesystem()方法。你可以通过传递第二个参数来选择虚拟文件系统或物理文件系统。无论如何,用法都是相同的。

$fs = $this->createFilesystem('/tmp', FilesystemInterface::VIRTUAL);

$fs = $this->createFilesystem('/tmp', FilesystemInterface::PHYSICAL);

信息:即使你选择物理文件系统并将/tmp定义为根目录,创建的环境也不会写入系统临时目录/tmp

创建目录和文件

你会得到一个实现了FilesystemInterface的对象

  • createDirectory()
  • createFile()
  • tearDown()

你可以使用完整的路径创建目录和文件。

$filesystem->createDirectory('path/to/diretory');
$filesystem->createFile('path/to/file.txt');

然后你会得到一个你可以与之工作的SplFileInfo对象。

清理环境

为了控制测试的清理过程,你需要调用tearDown()方法(例如,在你的TestCasetearDown()方法中)。

protected function tearDown()
{
    $this->filesystem->tearDown();
}

实际上,这仅在你使用物理文件系统时才需要,因为虚拟文件系统只存在于内存中,并且会自动删除。但为了保持一致性:坚持这种方法。