crimsonkissaki/mockmaker

无论你称它们为双胞胎、存根、模拟、部分、伪造还是其他什么,有时使用PHPUnit的mockBuilder、Mockery、Prophecy等模拟库并不能完全满足你的需求或期望。有时你只是需要一个具体的类实现来运行单元测试,或者是一个完整的端到端功能单元测试套件。

1.0.0 2015-05-08 21:52 UTC

This package is not auto-updated.

Last update: 2024-09-28 17:12:40 UTC


README

一个自动化的实体类"mock"文件生成器。

无论你称它们为双胞胎、存根、模拟、部分、伪造还是其他什么,有时使用PHPUnit的mockBuilder、Mockery、Prophecy等模拟库并不能完全满足你的需求或期望。有时你只是 需要 一个具体的类实现来运行单元测试的筛选器或一个完整的端到端功能单元测试套件。

MockMaker旨在简化生成特定于ORM实体的具体假对象的流程(因为这是我编写这个库最初要解决的问题)。

为什么使用MockMaker

让我们面对现实,如果你曾经继承了一个包含二十多个实体类的大代码库,这些类需要快速设置以用于单元/功能测试,你就会知道建立这些类是多么大的一个麻烦。在文件之间跳来跳去,保持属性、方法和实体关系清晰,同时手动编写代码来生成这些模拟是非常耗时的。如果这些实体有非公共属性,那么动态数据设置会让人感到非常烦恼。再加上一些复杂的关联映射和一些相互关联的多对一关系,足以让你需要一杯烈酒。

因此,如果你是

  • 将遗留数据库迁移到ORM,并确保生成的实体将与现有的单元测试套件一起工作。
  • 继承了一个高度依赖Doctrine(或其他ORM)的项目,该项目包含几十个实体类,而且根本没有任何单元测试。
  • 在项目和一个模式更新中,所有东西都改变了!
  • 在一个需要大量涉及数据库访问的功能测试的项目中工作。
  • 需要设置具有随机数据的有效实体,以用于加载/单元/功能测试。
  • 高效地懒散,并喜欢让脚本做苦力工作。

MockMaker可能能够帮助你。

MockMaker做什么

在核心上,MockMaker接收文件和/或目录的列表,并为任何可实例化的类生成模拟'种子'文件。这些模拟文件可以用于生成用于测试的实体类的即时实例。接口和抽象类不适用。

  • 如果属性是公共的,它就设置值。
  • 如果属性是私有/受保护的/静态的,它将使用反射来动态设置这些值。
  • 如果有类型提示的类或默认值,它将自动包含/提示在默认值部分,以帮助保持事物的清晰。
  • 包括一个默认设置选项,允许配置"骨架"对象创建,以便在只需要一个有效实例而不管数据的情况下使用。

灵活且可扩展,种子代码可以轻松地根据你的特定项目进行更改,因此,在初始设置之后,你可以重新运行MockMaker以添加任何新实体或更改现有实体,而无需太多的麻烦。更重要的是,一旦MockMaker创建了你的文件,它就完成了。你不需要将其包含在代码库中,可以像使用其他项目类一样使用生成的文件。

请注意,我认为MockMaker处于Beta阶段。还有一些功能尚未完全工作,但我需要将其发布并可用于测试,通过将它们拉入Composer。

安装

通过Composer

    composer require-dev crimsonkissaki/mockmaker:dev-master

注意 由于我尚未将仓库上传到packagist,您需要更新您的composer.json文件,添加一个用于此GitHub仓库的仓库条目。当我对它在大多数用例中的工作情况感到满意时,我会这样做。

        {
            "type": "vcs",
            "url": "https://github.com/crimsonkissaki/MockMaker.git"
        }

MockMaker配置与使用

我已经尝试包含足够的配置选项,以覆盖大多数用例。

MockMaker通过人类可读的设置进行配置,应该对几乎所有人都很有意义。希望如此。

MockMaker支持方法链,因此几乎所有方法都可以堆叠。例外是verifySettings()testRegexPatterns()createMocks()方法,因为它们是基于配置设置返回实际结果。

创建一个新的MockMaker实例。

use MockMaker\MockMaker;

$mocks = new MockMaker();

定义您想要模拟的文件。

// @param   string|array    $files  Fully qualified file paths
$mocks->mockTheseEntities($files);

让MockMaker为您解析目录并找到.php文件。

// @param   string|array   $dirs    Fully qualified directory paths
$mocks->mockEntitiesInDirectory($dirs);

告诉MockMaker递归地检查读取的目录中的文件。

默认设置为false

$mocks->recursively();

告诉MockMaker您的项目根路径在哪里。

MockMaker尝试自动检测此路径,因此只有在检测失败时才需要设置。

// @param   string  $path   Fully qualified path to your project's root directory
$mocks->setProjectRootPath($path);

让MockMaker为您创建并保存模拟文件。

如果没有指定目录,模拟代码将以字符串形式返回,您可以从任何您转储的地方复制/粘贴/输出。

// @param   string  $dir    Fully qualified directory path
$mocks->saveMockFilesIn($dir);

让MockMaker为模拟文件创建单元测试。

单元测试非常基本,只确保模拟返回实体类的有效实例。

// @param   string  $dir    Fully qualified directory path
$mocks->createUnitTests();

让MockMaker为您保存模拟文件。

如果没有指定目录,模拟代码将以字符串形式返回,您可以从任何您转储的地方复制/粘贴/输出。

// @param   string  $dir    Fully qualified directory path
$mocks->saveUnitTestsIn($dir);

默认情况下,MockMaker会按照您指定的读取目录的目录结构复制文件,如果它正在递归地读取它们。

如果您希望它们被合并到一个目录中,请使用此选项。

$mocks->ignoreDirectoryStructure();

默认情况下,MockMaker不会覆盖您已指示重新模拟的现有文件(这是为了防止意外覆盖已使用默认值自定义的文件并丢失您的作品)。

使用此选项,您承认您是一个负责任的开发者,并理解“覆盖”的含义及其后果。

$mocks->overwriteMockFiles();

默认情况下,MockMaker不会覆盖您已指示重新模拟的现有单元测试(这是为了防止意外覆盖已自定义的文件并丢失您的作品)。

使用此选项,您承认您是一个负责任的开发者,并理解“覆盖”的含义及其后果。

$mocks->overwriteUnitTestFiles();

定义一个正则表达式模式,用于排除文件被处理(默认允许)。

这将覆盖通过includeFilesWithFormat()包含的任何文件。

这将被应用于通过mockTheseEntities()mockEntitiesInDirectory()获得的任何文件。

// @param   string  $regex   Regex pattern
$mocks->excludeFilesWithFormat($regex);

定义一个正则表达式模式,用于包含文件被处理(默认拒绝)。

这将被应用于通过mockTheseEntities()mockEntitiesInDirectory()获得的任何文件。

// @param   string  $regex   Regex pattern
$mocks->includeFilesWithFormat($regex);

如果您不想使用默认的模拟名称格式{FileName}Mock,则可以在此处指定新的格式。如果您想将文件/类名放在字符串中,只需将%FileName%放在其中即可。

例如,如果您想MockedMyEntity是类名,请使用'Mocked%FileName%'

// @param   string  $format     Format for mock file names and class names
$mocks->saveMocksWithFileNameFormat($format);

MockMaker将努力确定生成的模拟类的适当命名空间,但这取决于能否访问Composer。

如果自动命名空间不正确,您可以在下面指定一个“基本命名空间”。

如果您从具有子目录的目录递归地读取文件,MockMaker应该能够相应地调整命名空间。

如果您不使用PSR-4或PSR-0命名空间,您会遇到麻烦。

// @param   string  $namespace  Base namespace to use for mocks
$mocks->useBaseNamespaceForMocks($namespace);

返回包含您的MockMaker配置设置结果的对象。

这在您实际上让MockMaker编写内容之前检查事物很有用。

// @return  ConfigData
$mocks->verifySettings();

返回由您的正则表达式模式过滤的文件的关联数组。

$results = array( 'include' => [], 'exclude' => [], 'workable' => [] );

// @return  array
$mocks->testRegexPatterns();

生成模拟文件代码,可以返回它或将它写入文件。

// @return  string
$mocks->createMocks();

用例示例

MockMaker以这种方式设置,选项可以按任何顺序添加,所以不用担心它在堆栈中的位置。

模拟单个文件并返回代码作为字符串

use MockMaker\MockMaker;

$rootDir = '/Applications/XAMPP/xamppfiles/htdocs/yourproject/src/';
$projectDir = $rootDir . 'Project/Bundle/MyAwesomeBundle/';

$mm = new MockMaker();
$code = $mm->mockTheseEntities($projectDir . 'Entity/TestEntity.php')
    ->createMocks();
    
echo $code;

模拟除Doctrine的"{EntityName}Repository"类之外的所有内容

use MockMaker\MockMaker;

$rootDir = '/Applications/XAMPP/xamppfiles/htdocs/yourproject/src/';
$projectDir = $rootDir . 'Project/Bundle/MyAwesomeBundle/';

$mm = new MockMaker();
$mm->mockEntitiesInDirectory($projectDir . 'Entity')
    ->recursively()
    ->exclude('/Repository$/')
    ->saveMockFilesIn($projectDir . 'Tests/Mocks/Entity')
    ->saveUnitTestsIn($projectDir . 'Tests/Mocks/EntityTests')
    ->overwriteMockFiles()
    ->overwriteUnitTestFiles()
    ->createMocks();

模拟文件用法

设置默认值

你确实需要做一些工作,但至少把它保持在最小。默认情况下,MockMaker将为任何实体的属性设置一个“最佳猜测”的默认值数组,无论其可见性(常量/公共/私有/受保护/静态)。

return array(
    'propertyName' => 'propertyName value',
    'propertyName2' => 'John Q. Public',
    'propertyName3' => new \stdClass(),
    'propertyName4' => new SimpleEntity(),
);

如果在setter中类型提示默认值,在__construct()中分配,或在类属性声明中直接设置,它将自动包括在内。根据需要更改或添加default值。

这种设置允许特殊情况的实体具有条件属性(例如具有'disabledOn'属性的客户实体),如果定义了这些属性,将影响业务逻辑。从该数组中省略或删除的属性在设置模拟时将不会使用,除非你明确告诉它使用。如果你出于某种原因不想设置默认属性之一,稍后会有一个选项覆盖。

基本模拟

当你只需要一个品牌通用的产品时。

如果你只需要一个具有最小必需属性的预填充默认值的对象,以确保其可行性

use Path\To\EntityMock;

$mock = EntityMock::getMock();

模拟文件返回一个类的实例,其中只有数组中定义的属性具有你预定义的默认值。不在数组中的属性将被忽略。

高级模拟

因为成品不适合。

这特别适用于运行时自定义实体值,这样你可以使用PHPUnit数据提供者、工厂或随机数据生成器来创建尽可能多的定制实体实例,以满足你的测试需求。

如果你需要覆盖特定属性(的)的默认值,或包括默认值数组中省略的属性

use Path\To\EntityMock;
use Path\To\CustomerEntityMock;

// format is 'propertyName' => 'desiredValue'
$properties = array(
    'customerOrders',    // no defined value, defaultly assigned NULL
    'customerId' => 1,
    'customerName' => 'John Doe',
    'customerData' => CustomerEntityMock::getMock()
);
$mock = EntityMock::getMock( $properties );

当模拟返回时,数组中的'propertyName'属性将设置为'desiredValue'。如果没有设置值,则属性将自动设置为NULL

但是,如果你在默认数组中有值,因为它是一些那些通常需要的但现在真的很不合适的东西?我们已经为你准备好了。

use Path\To\EntityMock;
use Path\To\CustomerEntityMock;

// format is 'propertyName' => 'desiredValue'
$properties = array(
    'customerOrders',
    'customerId' => 1,
    'customerName' => 'John Doe',
    'customerData' => CustomerEntityMock::getMock()
);
$ignore = array( 'customerOrders', 'customerPhone', 'disabledOn' );
$mock = EntityMock::getMock( $properties, $ignore );

当模拟返回时,任何在$ignore数组中定义的属性都将被...忽略。即使它们在默认数组中或在$properties数组中传递,也不会对它们做任何事情。

已知错误和问题

  • 自定义CodeWorkers和MockFileTemplates尚未完全测试。
  • 目前尚未在packagist上工作,因为我尚未上传到那里。现在仍在测试阶段。

未来改进

  • 自定义模拟/单元测试模板和数据点工作者
  • 自动创建一个允许你通过单个类拉取模拟的'MockManager'文件。
  • ORM感知
    • 能够解析ORM特定注释,以获取数据类型提示和类关系。
  • 资源感知的“遗传”模拟生成(依赖于ORM功能)
    • 实体属性是链接到其他实体的(子类、关系等),它们的默认值将设置为该实体的模拟。
    • 如果那些模拟的实体不存在,它将将它们添加到模拟队列中。
  • 数据种子
    • 能够连接到现有的数据库,并拉取N条记录用于生成的“数据种子”文件,这将允许你传递一个参数到模拟构造函数,并在你的模拟中预填充实际有效的测试数据。
      • 例如,如果你想有一个预填充已知良好数据库数据的CustomerEntity,你可以传递一个'property'=>'value'对('id'=>1),然后它将返回匹配该标准的条目。
      • 仅适用于数组迭代器类型(一对多)关系时,才能使用多个条目。
  • 可能与其他“lorum ipsum”生成器集成,以自动填充实体属性的值。
  • 实体映射器:以易于阅读的格式显示各种实体之间的关系