{"id":13622659,"url":"https://github.com/Codeception/AspectMock","last_synced_at":"2025-04-15T09:33:16.677Z","repository":{"id":38427895,"uuid":"11553589","full_name":"Codeception/AspectMock","owner":"Codeception","description":"The most powerful and flexible mocking framework for PHPUnit / Codeception.","archived":false,"fork":false,"pushed_at":"2024-11-27T06:38:40.000Z","size":520,"stargazers_count":786,"open_issues_count":41,"forks_count":130,"subscribers_count":37,"default_branch":"master","last_synced_at":"2025-04-13T10:25:08.348Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Codeception.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-07-20T21:53:48.000Z","updated_at":"2025-04-08T18:16:34.000Z","dependencies_parsed_at":"2022-07-12T15:30:39.373Z","dependency_job_id":"303efd4a-5d4b-450c-97fb-fd4cd57e58cd","html_url":"https://github.com/Codeception/AspectMock","commit_stats":{"total_commits":215,"total_committers":55,"mean_commits":3.909090909090909,"dds":0.6651162790697674,"last_synced_commit":"bec82f5bafac742ccb33d3a0c527c13e3188afe5"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FAspectMock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FAspectMock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FAspectMock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FAspectMock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Codeception","download_url":"https://codeload.github.com/Codeception/AspectMock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249043112,"owners_count":21203417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:22.380Z","updated_at":"2025-04-15T09:33:16.662Z","avatar_url":"https://github.com/Codeception.png","language":"PHP","readme":"AspectMock\n==========\n\nAspectMock is not an ordinary PHP mocking framework.\nWith the power of Aspect Oriented programming and the awesome [Go-AOP](https://github.com/goaop/framework) library,\nAspectMock allows you to stub and mock practically anything in your PHP code!\n\n**Documentation** | [Test Doubles Builder](https://github.com/Codeception/AspectMock/blob/master/docs/Test.md) | [ClassProxy](https://github.com/Codeception/AspectMock/blob/master/docs/ClassProxy.md) | [InstanceProxy](https://github.com/Codeception/AspectMock/blob/master/docs/InstanceProxy.md) | [FuncProxy](https://github.com/Codeception/AspectMock/blob/master/docs/FuncProxy.md)\n\n[![Actions Status](https://github.com/Codeception/AspectMock/workflows/CI/badge.svg)](https://github.com/Codeception/AspectMock/actions)\n[![Latest Stable Version](https://poser.pugx.org/codeception/aspect-mock/v/stable.png)](https://packagist.org/packages/codeception/aspect-mock)\n[![Total Downloads](https://poser.pugx.org/codeception/aspect-mock/downloads)](https://packagist.org/packages/codeception/aspect-mock)\n[![Monthly Downloads](https://poser.pugx.org/codeception/aspect-mock/d/monthly)](https://packagist.org/packages/codeception/aspect-mock)\n[![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)\n\n## Motivation\n\nPHP is a language that was not designed to be testable. Really. \nHow would you fake the `time()` function to produce the same result for each test call?\nIs there any way to stub a static method of a class? Can you redefine a class method at runtime?\nDynamic languages like Ruby or JavaScript allow us to do this. \nThese features are essential for testing. AspectMock to the rescue!\n\nThousands of lines of untested code are written everyday in PHP.\nIn most cases, this code is not actually bad, \nbut PHP does not provide capabilities to test it. You may suggest rewriting it from scratch following test driven design practices and use dependency injection wherever possible. Should this be done for stable working code? Well, there are much better ways to waste time.\n\nWith AspectMock you can unit-test practically any OOP code. PHP powered with AOP incorporates features of dynamic languages we have long been missing. There is no excuse for not testing your code.\nYou do not have to rewrite it from scratch to make it testable. Just install AspectMock with PHPUnit or Codeception and try to write some tests. It's really, really simple!\n\n\n## Features\n\n* Create test doubles for **static methods**.\n* Create test doubles for **class methods called anywhere**.\n* Redefine methods on the fly.\n* Simple syntax that's easy to remember.\n\n## Code Pitch\n\n#### Allows stubbing and mocking of static methods.\n\nLet's redefine static methods and verify their calls at runtime.\n\n``` php\n\u003c?php\n\nfunction testTableName()\n{\n\t$this-\u003eassertSame('users', UserModel::tableName());\t\n\t$userModel = test::double('UserModel', ['tableName' =\u003e 'my_users']);\n\t$this-\u003eassertSame('my_users', UserModel::tableName());\n\t$userModel-\u003everifyInvoked('tableName');\t\n}\n```\n\n#### Allows replacement of class methods.\n\nTesting code developed with the **ActiveRecord** pattern. Does the use of the ActiveRecord pattern sound like bad practice? No. But the code below is untestable in classic unit testing.\n\n``` php\n\u003c?php\n\nclass UserService {\n    function createUserByName($name) {\n    \t$user = new User;\n    \t$user-\u003esetName($name);\n    \t$user-\u003esave();\n    }\n}\n```\n\nWithout AspectMock you need to introduce `User` as an explicit dependency into class `UserService` to get it tested.\nBut lets leave the code as it is. It works. Nevertheless, we should still test it to avoid regressions.\n\nWe don't want the `$user-\u003esave` method to actually get executed, as it will hit the database.\nInstead we will replace it with a dummy and verify that it gets called by `createUserByName`:\n\n``` php\n\u003c?php\n\nfunction testUserCreate()\n{\n\t$user = test::double('User', ['save' =\u003e null]);\n\t$service = new UserService;\n\t$service-\u003ecreateUserByName('davert');\n\t$this-\u003eassertSame('davert', $user-\u003egetName());\n\t$user-\u003everifyInvoked('save');\n}\n```\n\n#### Intercept even parent class methods and magic methods\n\n``` php\n\u003c?php\n\n// User extends ActiveRecord\nfunction testUserCreate()\n{\n\t$AR = test::double('ActiveRecord', ['save' =\u003e null]));\n\ttest::double('User', ['findByNameAndEmail' =\u003e new User(['name' =\u003e 'jon'])])); \n\t$user = User::findByNameAndEmail('jon','jon@coltrane.com'); // magic method\n\t$this-\u003eassertSame('jon', $user-\u003egetName());\n\t$user-\u003esave(['name' =\u003e 'miles']); // ActiveRecord-\u003esave did not hit database\n\t$AR-\u003everifyInvoked('save');\n\t$this-\u003eassertSame('miles', $user-\u003egetName());\n}\n```\n\n#### Override even standard PHP functions\n\n``` php\n\u003c?php\n\nnamespace demo;\ntest::func('demo', 'time', 'now');\n$this-\u003eassertSame('now', time());\n```\n\n#### Beautifully simple\n\nOnly 4 methods are necessary for method call verification and one method to define test doubles:\n\n``` php\n\u003c?php\n\nfunction testSimpleStubAndMock()\n{\n\t$user = test::double(new User, ['getName' =\u003e 'davert']);\n\t$this-\u003eassertSame('davert', $user-\u003egetName());\n\t$user-\u003everifyInvoked('getName');\n\t$user-\u003everifyInvokedOnce('getName');\n\t$user-\u003everifyNeverInvoked('setName');\n\t$user-\u003everifyInvokedMultipleTimes('setName',1);\n}\n```\n\nTo check that method `setName` was called with `davert` as argument.\n\n``` php\n\u003c?php\n$user-\u003everifyMethodInvoked('setName', ['davert']);\n```\n\n## Wow! But how does it work?\n\nNo PECL extensions is required. The [Go! AOP](http://go.aopphp.com/) library does the heavy lifting by patching autoloaded PHP classes on the fly. By introducing pointcuts to every method call, Go! allows intercepting practically any call to a method. AspectMock is a very tiny framework consisting of only 8 files using the power of the [Go! AOP Framework](http://go.aopphp.com/). Check out Aspect Oriented Development and the Go! library itself.\n\n## Requirements\n\n* `PHP 8.2+`\n* `Go! AOP 4.x`\n\n## Installation\n\n### 1. Add aspect-mock to your composer.json.\n\n(lines with goaop @dev are needed depending on the `minimum-stability` in your composer settings)\n\n```\n{\n\t\"require-dev\": {\n\t\t\"codeception/aspect-mock\": \"*\",\n\t\t\"goaop/framework\": \"@dev\",\n\t\t\"goaop/parser-reflection\": \"@dev\"\n\t}\n}\n```\n\n### 2. Install AspectMock with Go! AOP as a dependency.\n\n```\nphp composer.phar update\n```\n\n## Configuration\n\nInclude `AspectMock\\Kernel` class into your tests bootstrap file. \n\n### With Composer's Autoloader\n\n``` php\n\u003c?php\n\ninclude __DIR__.'/../vendor/autoload.php'; // composer autoload\n\n$kernel = \\AspectMock\\Kernel::getInstance();\n$kernel-\u003einit([\n    'debug' =\u003e true,\n    'includePaths' =\u003e [__DIR__.'/../src']\n]);\n```\n\nIf your project uses Composer's autoloader, that's all you need to get started.\n\n### With Custom Autoloader\n\nIf you use a custom autoloader (like in Yii/Yii2 frameworks), you should explicitly point AspectMock to modify it:\n\n``` php\n\u003c?php\n\ninclude __DIR__.'/../vendor/autoload.php'; // composer autoload\n\n$kernel = \\AspectMock\\Kernel::getInstance();\n$kernel-\u003einit([\n    'debug' =\u003e true,\n    'includePaths' =\u003e [__DIR__.'/../src']\n]);\n$kernel-\u003eloadFile('YourAutoloader.php'); // path to your autoloader\n```\n\nLoad all autoloaders of your project this way, if you do not rely on Composer entirely.\n\n### Without Autoloader\n\nIf it still doesn't work for you... \n\nExplicitly load all required files before testing:\n\n\n``` php\n\u003c?php\n\ninclude __DIR__.'/../vendor/autoload.php'; // composer autoload\n\n$kernel = \\AspectMock\\Kernel::getInstance();\n$kernel-\u003einit([\n    'debug' =\u003e true,\n    'includePaths' =\u003e [__DIR__.'/../src']\n]);\nrequire 'YourAutoloader.php';\n$kernel-\u003eloadPhpFiles('/../common');\n```\n\n### Customization\n\nThere are a few options you can customize setting up AspectMock. All them are defined in Go! Framework.\nThey might help If you still didn't get AspectMock running on your project.\n\n* `appDir` defines the root of web application which is being tested. All classes outside the root will be replaced with the proxies generated by AspectMock. By default it is a directory in which `vendor` dir of composer if located. **If you don't use Composer** or you have custom path to composer's vendor's folder, you should specify appDir\n* `cacheDir` a dir where updated source PHP files can be stored. If this directory is not set, proxie classes will be built on each run. Otherwise all PHP files used in tests will be updated with aspect injections and stored into `cacheDir` path.\n* `includePaths` directories with files that should be enhanced by Go Aop. Should point to your applications source files as well as framework files and any libraries you use..\n* `excludePaths` a paths in which PHP files should not be affected by aspects. **You should exclude your tests files from interception**.\n\nExample:\n\n\n``` php\n\u003c?php\n\n$kernel = \\AspectMock\\Kernel::getInstance();\n$kernel-\u003einit([\n    'appDir'    =\u003e __DIR__ . '/../../',\n    'cacheDir'  =\u003e '/tmp/myapp',\n    'includePaths' =\u003e [__DIR__.'/../src']\n    'excludePaths' =\u003e [__DIR__] // tests dir should be excluded\n]);\n```\n\n[More configs for different frameworks](https://github.com/Codeception/AspectMock/wiki/Example-configs).\n\n**It's pretty important to configure AspectMock properly. Otherwise it may not work as expected or you get side effects. Please make sure you included all files that you need to mock, but your test files as well as testing frameworks are excluded.**\n\n\n## Usage in PHPUnit\n\nUse newly created `bootstrap` in your `phpunit.xml` configuration. Also disable `backupGlobals`:\n\n``` xml\n\u003cphpunit bootstrap=\"bootstrap.php\" backupGlobals=\"false\"\u003e\n```\n\nClear the test doubles registry between tests.\n\n``` php\n\u003c?php\n\nuse AspectMock\\Test as test;\n\nclass UserTest extends \\PHPUnit_Framework_TestCase\n{\n    protected function tearDown()\n    {\n        test::clean(); // remove all registered test doubles\n    }\n\n    public function testDoubleClass()\n    {\n        $user = test::double('demo\\UserModel', ['save' =\u003e null]);\n        \\demo\\UserModel::tableName();\n        \\demo\\UserModel::tableName();\n        $user-\u003everifyInvokedMultipleTimes('tableName',2);\n    }\n```\n\n## Usage in Codeception.\n\nInclude `AspectMock\\Kernel` into `tests/_bootstrap.php`.\nWe recommend including a call to `test::clean()` from your `CodeHelper` class:\n\n``` php\n\u003c?php\n\nnamespace Codeception\\Module;\n\nclass CodeHelper extends \\Codeception\\Module\n{\n\tfunction _after(\\Codeception\\TestCase $test)\n\t{\n\t\t\\AspectMock\\Test::clean();\n\t}\n}\n```\n\n## Improvements?\n\nThere is guaranteed to be room for improvements. This framework was not designed to do everything you might ever need (see notes below). But if you feel like you require a feature, please submit a Pull Request. It's pretty easy since there's not much code, and the Go! library is very well documented.\n\n## Credits\n\nFollow [**@codeception**](http://twitter.com/codeception) for updates.\n\nDeveloped by **Michael Bodnarchuk**.\n\nLicense: **MIT**.\n\nPowered by [Go! Aspect-Oriented Framework](http://go.aopphp.com/)\n","funding_links":[],"categories":["Testing","测试","测试 Testing","Table of Contents","PHP","目录","测试( Testing )"],"sub_categories":["Testing","测试 Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCodeception%2FAspectMock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCodeception%2FAspectMock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCodeception%2FAspectMock/lists"}