crimsonkissaki/mockmaker 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

crimsonkissaki/mockmaker

Composer 安装命令:

composer require --dev crimsonkissaki/mockmaker

包简介

Whether you call them doubles, stubs, mocks, partials, fakes, or something else there are times when a mocking library such as PHPUnit's mockBuilder, Mockery, Prophecy, etc. just doesn't do exactly what you need or want. Sometimes you just need a concrete class implementation to run through the unit

README 文档

README

An automated entity class "mock" file seeder.

Whether you call them doubles, stubs, mocks, partials, fakes, or something else there are times when a mocking library such as PHPUnit's mockBuilder, Mockery, Prophecy, etc. just doesn't do exactly what you need or want. Sometimes you just need a concrete class implementation to run through the unit test wringer or a full end to end functional unit test suite.

MockMaker aims to simplify the process of generating concrete fake object with a particular emphasis on ORM entities (because that's the original problem I wrote this library so solve).

Why use MockMaker:

Let's face it, if you've ever inherited a large code base with a couple dozen entity classes that need to quickly be set up for use in unit/functional tests you know how big of a PITA standing those up can be. Jumping back and forth between files, keeping the properties, methods, and entity relationships straight while manually writing up the code to generate those mocks is horribly time consuming. If those entities have non-public properties it makes dynamic data setting annoying as hell. Toss in some heavy association mapping and a few many-to-one relationships that circle around each other and it's enough to make you need a stiff drink.

So, if you are:

  • Porting a legacy database over to an ORM and need to make sure the generated entities will work with your existing unit test suite.
  • Inheriting a project that relies heavily on Doctrine (or another ORM) with a few dozen entity classes, and there's NO unit testing whatsoever.
  • On a project and a schema update just changed all the things!
  • Working on a project that requires extensive functional tests involving database access.
  • Need to stand up valid entities with randomized data for load/unit/functional testing.
  • Efficiently lazy and prefer to let a script do the grunt work.

MockMaker might be able to lend you a hand.

What MockMaker does:

At its core, MockMaker takes a list of files and/or directories and generates mock 'seeder' files for any instantiable classes. Those mock files can be used to generate on-the-fly instantiations of entity classes for use in testing. Interfaces and Abstracts need not apply.

  • If the property is public, it just sets the value.
  • If the property is private/protected/static it uses reflection to dynamically set those values.
  • If there is a typehinted class or a default value it will be automatically included/hinted at in the default values section to help keep things straight.
  • A default settings option is included that allows for configuration of 'bare bones' object creation for those times when you just need a valid instance, regardless of data.

Flexible and extendable, the seed code can be altered to suit your particular project with relative ease, so after the initial setup you can re-run MockMaker for any new entities that get added in, or existing entities that change with little to no fuss. What's more, once MockMaker has made your files it's done. You don't have to include it in your code base and can use the generated files like any other project class.

Please be aware, I consider MockMaker to be in Beta stage. There is still some functionality that is not completely working, but I needed to get this up and available for testing with pulling into Composer.

Installation:

Through Composer:

    composer require-dev crimsonkissaki/mockmaker:dev-master

NOTE You will have to update your composer.json file with a repository entry for this github repo as I have not yet uploaded the repo to packagist. I will do so when I'm happy that it will work on most use-cases.

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

MockMaker Configuration & Usage:

I've tried to include enough configuration options to cover a good majority of use cases.

MockMaker is configured through human-readable settings that should make sense to just about everyone. Hopefully.

MockMaker supports method chaining, so virtually all of them can be stacked up. Exceptions to this are the verifySettings(), testRegexPatterns(), and createMocks() methods since they are for returning actual results based on configuration settings.

Create a new MockMaker instance.

use MockMaker\MockMaker;

$mocks = new MockMaker();

Define files you want mocked.

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

Have MockMaker parse directories and find .php files for you.

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

Tell MockMaker to recursively check through the read directories for files.

The default setting is false

$mocks->recursively();

Tell MockMaker where your project's root path is.

MockMaker tries to auto-detect this, so you only have to set it if that's failing.

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

Have MockMaker create & save the mock files for you.

If there is no directory specified here, the mock code will be returned as a string you can copy/paste/stdout from wherever you dump it.

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

Have MockMaker create unit tests for the mock files.

Unit tests are very basic and only make sure the mocks return valid instances of the entity class

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

Have MockMaker save the mock files for you.

If there is no directory specified here, the mock code will be returned as a string you can copy/paste/stdout from wherever you dump it.

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

By default, MockMaker will replicate the directory structure of the read directories you specify as it's saving files, if it's reading through them recursively.

If you'd rather they get lumped together into one directory, use this.

$mocks->ignoreDirectoryStructure();

By default MockMaker will not overwrite existing files if you've told it to re-mock something that's already been done. (This is to prevent accidentally overwriting files that have already been customized with defaults and losing your work.)

By using this option you avow you're a responsible developer and understand what 'overwrite' means and what its consequences are.

$mocks->overwriteMockFiles();

By default MockMaker will not overwrite existing unit tests if you've told it to re-mock something that's already been done. (This is to prevent accidentally overwriting files that have already been customized and losing your work.)

By using this option you avow you're a responsible developer and understand what 'overwrite' means and what its consequences are.

$mocks->overwriteUnitTestFiles();

Define a regex pattern used to exclude files from being processed. (default allow)

This will override any files included through includeFilesWithFormat().

This will be applied to any files obtained through mockTheseEntities() or mockEntitiesInDirectory().

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

Define a regex pattern used to include files from being processed. (default deny)

This will be applied to any files obtained through mockTheseEntities() or mockEntitiesInDirectory().

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

If you don't want the default mock name format of {FileName}Mock, then you can specify a new format here. If you want the file/class name in the string just put %FileName% somewhere in there.

e.g. If you want MockedMyEntity to be the class name, use 'Mocked%FileName%'

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

MockMaker will make a good effort at determining a proper class namespace for the mock classes it generates, but that depends on having access to Composer.

If the automatic namespaces are not right, you can specify a 'base namespace' here.

If you're recursively reading files from a directory with sub-directories, MockMaker should be able to adjust the namespace accordingly.

If you don't use PSR-4 or PSR-0 namespaces, you're gonna have a bad time.

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

Returns an object with the results of your MockMaker configuration settings.

This is good for checking things out before you actually have MockMaker write stuff.

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

Returns an associative array of files that are filtered out by your regex patterns.

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

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

Generate the mock file code, and either return it or write it to files.

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

Use Case Examples:

MockMaker is set up in such a way that options can be added in any order, so don't worry about where in the stack it ends up.

Mock single file and return code as string:

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;

Mock everything excluding Doctrine's "{EntityName}Repository" classes:

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();

Mock File Usage:

Setting Defaults:

You do have to do SOME work, but at least it's kept to a minimum. By default, MockMaker will set up a 'best guess' array of defaults for any entity properties, regardless of visibility (constant/public/private/protected/static).

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

If a default value is typehinted in the setter, assigned in a __construct(), or set in-line in the class property declaration, it is included automatically. Change or add the default value as appropriate.

This setup allows for special cases of entities that have conditional properties (e.g. a customer entity with a 'disabledOn' property) that, if defined, will influence business logic. Properties that are omitted/deleted from this array will not be used when setting up a mock unless you specifically tell it to. And if for some reason you don't want one of the default properties to be set, that option is covered later on.

'Basic' Mocks:

When store-brand generic is all you need.

If you just need an object with pre-populated default values using the minimum required properties for viability:

use Path\To\EntityMock;

$mock = EntityMock::getMock();

The mock file returns an instance of the class with only the properties defined in the array having your predefined default values. Properties NOT in the array will be ignored.

'Advanced' Mocks:

Because off-the-rack doesn't fit quite right.

This is especially useful for run-time customization of entity values so you can use PHPUnit data providers, factories, or random data generators to create as many customized entity instances as your tests require.

If you need to override a default value for particular propert(y|ies), or include a property that is omitted from the defaults array:

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 );

When the mock is returned, the 'propertyName' property in the array will be set to 'desiredValue'. If no value is set then the property will automatically be set to NULL.

But, what if you have a value in the default array because it's one of those things that is usually-needed-but-just-not-right-now-dangit? We've got you covered.

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 );

When the mock is returned, any properties defined in the $ignore array will be ... ignored. Even if they're in the defaults array or passed in with the $properties array, nothing will happen to them.

Known Bugs & Issues

  • Custom CodeWorkers and MockFileTemplates haven't been fully tested.
  • Currently not working on packagist, as I haven't uploaded it to them yet. Still testing phase for now.

Future Improvements:

  • Custom mock/unit test templates and datapoint workers
  • Automatic creation of a 'MockManager' file that allows you to pull in mocks through a single class.
  • ORM awareness:
    • Ability to parse ORM specific annotations for data type hints and class relationships.
  • Resource aware "hereditary" mock generation (dependent on ORM feature):
    • Entity properties that are links to other entities (children classes, relationships, etc) will have their default value set to the mock of that entity.
    • If those mocked entities do not exist it will add them to a queue for mocking.
  • Data seeder:
    • Ability to connect to an existing DB and pull in N records for a generated "data seeder" file, which will allow you to pass in a parameter to a mock construct and get actual valid testing data pre-populated in your mock.
      • E.g. if you want a CustomerEntity pre-populated with known-good DB data, you could pass in a 'property'=>'value' pair ('id'=>1), and it will return the entr(y|ies*) that match that criteria.
      • *Multiple entries would only work for ArrayIterator-type (one-to-many) relationships.
  • Possible integration with a "lorum ipsum" generator to auto-fill values for entity properties.
  • Entity Mapper: show relationships between various entities in nice human-readable format

crimsonkissaki/mockmaker 适用场景与选型建议

crimsonkissaki/mockmaker 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 44 次下载、GitHub Stars 达 4, 最近一次更新时间为 2015 年 05 月 08 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「testing」 「library」 「BDD」 「TDD」 「test」 「mock」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 crimsonkissaki/mockmaker 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 crimsonkissaki/mockmaker 我们能提供哪些服务?
定制开发 / 二次开发

基于 crimsonkissaki/mockmaker 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 44
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 4
  • 点击次数: 26
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 4
  • Watchers: 2
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-05-08