trismegiste/phpunit-assert-solid
Composer 安装命令:
composer require trismegiste/phpunit-assert-solid
包简介
Assert your SOLID compliance and coding good pratices in unit tests
README 文档
README
Don't remind others the SOLID guidances, do it programatically... with an axe 😄
When
Today is the day your programer's life has changed ! You're tired to endlessly remind your team/github-users to follow SOLID principles and Demeter's Law ? Now it's over. (With such a statement, I wage you'll read the next paragraph)
What
It's a library of assertions for the programer's best friend a.k.a : PHPUnit. These assertions are about how classes don't break some elementary rules of OOP, mainly the SOLID guidances and Demeter's Law.
How
This library does nothing by itself. It is NOT a static code analyzer, it is more a helper for putting assertions on SOLID compliance in unit tests. If a static code analyzer is a cure, this tool is a vaccine.
Add this lib to your project with composer :
$ composer.phar require --dev trismegiste/phpunit-assert-solid:dev-master
use the trait in your phpunit unit test class :
class ProjectTest extends PHPUnit_Framework_TestCase { use \Trismegiste\SolidAssert\GoodPractice; // <= this adds my new assertions // see examples/SimpleTest.php for a running example or below...
Why
Well, no one can force you to launch an analyzer unless some pre-commit hooks are set. Futhermore, even best analyzer cannot detect minor problem (specially with soft-typed language like PHP). Sometime those analyzers detect false negative so you could reasonably not put strict assertions based on those analyzers metrics.
Besides, in reality no one need a strict SOLID compliance for the whole project. Sometime, you break a rule because of speed/memory optimization. Sometime you break a rule because you don't want to over-engineer objects (which is bad practice too). Sometime you want to be very strict on THIS class and sometime you don't care about that quick & dirty package.
This tool is made for that.
Where
In the unit tests. So you're sure some simple assertions are checked.
Except this lib, no other tools are required. Example,
you made a beautiful loose-coupled class with all methods parameters type-hinted
with interfaces : add to your phpunit tests for this class
$this->assertInterfaceHintedParameter('MessyProject\MyBeautifulClass');
Who
YOU ! You decide how, how much and when one of your class must follow SOLID guidances. It does not cover all guidances but those assertions detect recurrent anti-patterns which could appear after many commits from others until you discover it is too late.
Isn't it great to know now your classes will keep their "design intent" even in chaotic environments ?
Examples Please !
class SimpleTest extends PHPUnit_Framework_TestCase { use \Trismegiste\SolidAssert\GoodPractice; // inserts new assertions public function testConcreteTypeHint() { // this one tests for loose-coupling : $this->assertInterfaceHintedParameter('BadProject\Case1'); } public function testHollywoodPrinciple() { // this one for Hollywood Principle : $this->assertHollywoodPrinciple('BadProject\Case4'); } // ... }
see full example in ./examples/SimpleTest.php
API
In all assertions, $fqcn means "fully qualified class name" as described as in PSR-0
assertTypeHintedMethodReturn($fqcn)
Asserts if all methods of a given class have a return type
assertInterfaceHintedParameter($fqcn)
Asserts if all methods parameters for a given class/interface are type-hinted with interface and not class. (loose-coupling)
assertNoMethodWithoutContract($fqcn)
Asserts if all public methods for a given class are first declared in an interface implemented by this class. (design by contract and ISP)
assertSmallApi($fqcn, $size)
Asserts if a given class has a small public api. Too many methods means too many responsibilities (SRP). Default size is 5
assertHollywoodPrinciple($fqcn)
Asserts there is no "new" nor Singleton calls in a given class. This ensures object dependencies are injected in this class, not created nor requested (DIP). A variant is "don't ask, tell".
assertLiskovCompliant($fqcn)
Asserts there is no "reflection tricks" which would undermine LSP : is_subclass_of, instanceof, method_exists and class_uses. There is exception for interfaces because, it is less dangerous and a common pattern.
assertNotStaticFactory($fqcn)
Asserts there is no new in a static method (typically singletons and static factories are use-case) because it is a violation of OCP (and DIP as edge-effect). Standard creational design patterns (Factory Method, Builder...) are not targeted by this assertion.
assertDemeterLawCompliant($fqcn)
Asserts a class only knows itself and its close neighbours (Law of Demeter). This checking is very crude so I can't garantee it will work efficiently. Experimental.
assertNoHiddenCoupling($fqcn)
Detects if parameters passed on to methods are actually objects without type-hinting which is a bad practice (design by contract violation). Can't detect php tricks like reflection and call_user_func*().
FAQ
How does it work ?
It uses Reflection and also nikic/PHPParser. This is the main advantage to integrate this tool into unit tests, because you have autoloading and it runs within a project unlike a static code analyzer.
Is it efficient ?
For performance concerns, this tool can make assertions only at the class scope, not the whole project scope, so some SOLID principles cannot be completely asserted, it's not magic. The goal is to avoid bad pratices to pollute an originally well-coded source not to be a SOLID-nazi. Think it like a "hack'n slash" for obvious bad practices.
By the way, if you think you've found another common OOP bad practice easy to detect, please contribute !
But you cannot prevent someone to remove one of your assertion ?
That's right, but the git-blame will show you who has knowingly removed it. It's much easier to track it and ignorance is no longer an excuse.
SOLID principles are not as strict as you assert it ! Or some assertions
are not in the right principle. That's not a question but I agree nonetheless. Many bad pratices don't break one principle but two or three. The best example is the Singleton antipattern : It breaks OCP because the singleton cannot be open for extension and it will break DIP elsewhere because clients of this singleton will call for the unique instance instead of being injected with the instance. So, remember the subtitle : "with an axe" 😄
Do you know that "testing does not mean freezing" ?
Yes, that's right. Perhaps, one would remove an assertion with good reason during project development. The goal is not to put all these assertions on every class of your project.
But I think assertInterfaceHintedParameter, assertNoHiddenCoupling should work almost everywhere. It is essential assertInterfaceHintedParameter will pass on every interface you declare. If your class is not a factory, assertHollywoodPrinciple should work too. assertNotStaticFactory is a way to "lock" a creational design pattern.
assertNoMethodWithoutContract is very strict so it will work on a few cases (Facade, Decorator, Repository...). assertLiskovCompliant should work on model classes because if you need reflection tricks within the model, you're doing it wrong. I realize an assertion like assertLiskovCompliant has little chance to pass on a controller or a dispatcher and assertHollywoodPrinciple will fail obviously on creational design pattens. So be smart.
Did you know one can easily workaround these assertions ?
That's right, for simplicity, this lib does not track hacks with ReflectionClass or call_user_func(). If somebody has the time to hack this lib, I think she must save that time and starts following SOLID.
Is this a hating machine for your co-workers/contributors ?
Not at all, it's just a 1337-pass-filter to speed-up code review. I've put effort into comprehensive error messages and by following the strict guidances of phpunit assertions, so debugging failed tests will be the standard procedure as usual.
Did you know that some code fixtures are invalid ? (example, calling parent
without a base class) ? Yes, I know. The code must compil, not run. Introspection with the PhpParser is sometime a pain in the ass to explore so I kept the code at the minimum. Beside, code is sometime not really valid so this lib must adapt without throwing an error each time a property is not declared.
Soundtracks used for coding this library
- Johann Sebastian Bach
- Dream Theater
- Ali Project
trismegiste/phpunit-assert-solid 适用场景与选型建议
trismegiste/phpunit-assert-solid 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 14.84k 次下载、GitHub Stars 达 9, 最近一次更新时间为 2014 年 12 月 03 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「phpunit」 「assert」 「analyzer」 「solid」 「demeter」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 trismegiste/phpunit-assert-solid 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 trismegiste/phpunit-assert-solid 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 trismegiste/phpunit-assert-solid 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
HiDev plugin for PHPUnit
Testing Suite For Lumen like Laravel does.
Log analyzer widget for yii2
Convenient array-related routine & better type casting
A cli tool which generates unit tests.
Runtime analysis tool for Doctrine ORM integrated into Symfony Web Profiler. Unlike static linters, it analyzes actual query execution at runtime to detect performance bottlenecks, security vulnerabilities, and best practice violations during development with real execution context and data.
统计信息
- 总下载量: 14.84k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 10
- 点击次数: 25
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2014-12-03