{"id":15284053,"url":"https://github.com/box/shmock","last_synced_at":"2025-04-12T23:21:01.493Z","repository":{"id":3435368,"uuid":"4487614","full_name":"box/shmock","owner":"box","description":"SHorthand for MOCKing in PHPUnit","archived":false,"fork":false,"pushed_at":"2017-02-28T21:26:35.000Z","size":500,"stargazers_count":34,"open_issues_count":3,"forks_count":8,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-26T17:21:18.660Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/box.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-05-29T21:11:36.000Z","updated_at":"2023-07-27T00:48:19.000Z","dependencies_parsed_at":"2022-09-04T03:11:33.926Z","dependency_job_id":null,"html_url":"https://github.com/box/shmock","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/box%2Fshmock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/box%2Fshmock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/box%2Fshmock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/box%2Fshmock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/box","download_url":"https://codeload.github.com/box/shmock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248644088,"owners_count":21138555,"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-09-30T14:48:55.375Z","updated_at":"2025-04-12T23:21:01.466Z","avatar_url":"https://github.com/box.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shmock (SHorthand for MOCKing)\n\n## What is this?\n\nShmock is a smooth alternative for creating mocks with PHPUnit that uses the mock/replay concept from EasyMock but uses closures to define the scope for mocking.\n\n  ```php\n\t\u003c?php\n\tnamespace Foo;\n\n\t/**\n\t * Here's a class we're trying to test yay.\n\t */\n\tclass Foo\n\t{\n\t\tprivate $foo = 0;\n\t\tprivate $incrementing_service = null;\n\n\t\tpublic function __construct(Incrementing_Service $incrementing_service)\n\t\t{\n\t\t\t$this-\u003eincrementing_service = $incrementing_service;\n\t\t}\n\n\t\tpublic function next_foo()\n\t\t{\n\t\t\t$this-\u003efoo = $this-\u003eincrementing_service-\u003eincrement($this-\u003efoo);\n\t\t\treturn $this-\u003efoo;\n\t\t}\n\t}\n\n\t/**\n\t * Our test case runs the same test case twice - once with the original PHPUnit mocking\n\t * syntax and a second time with Shmock syntax.\n\t */\n\tclass Foo_Test extends PHPUnit_Framework_TestCase\n\t{\n                use \\Shmock\\Shmockers; // This enables the use of the Shmock helper methods (replicated below)\n\n                public function test_phpunit_original_mocking_syntax()\n\t\t{\n\t\t\t// this is the original PHPUnit mock syntax\n\n\t\t\t$incrementing_service_mock = $this-\u003egetMock('\\Foo\\Incrementing_Service', array('increment'));\n\t\t\t$incrementing_service_mock-\u003eexpects($this-\u003eonce())\n\t\t\t\t-\u003emethod('increment')\n\t\t\t\t-\u003ewith($this-\u003eequalTo(0))\n\t\t\t\t-\u003ewill($this-\u003ereturnValue(1));\n\n\t\t\t$foo = new Foo($incrementing_service_mock);\n\t\t\t$this-\u003eassertEquals(1, $foo-\u003enext_foo(0));\n\t\t}\n\n\t\t/**\n\t\t * Create a shmock representation for $class_name and configure expected\n\t\t * mock interaction with $conf_closure\n\t\t * @return Shmock A fully configured mock object\n\t\t * @note You do not need this protected method if you use the Shmockers trait, shown above\n\t\t */\n\t\tprotected function shmock($class_name, $conf_closure)\n\t\t{\n\t\t\treturn \\Shmock\\Shmock::create_class($this, $class_name, $conf_closure);\n\t\t}\n\n\t\tpublic function test_shmock_syntax()\n\t\t{\n\t\t\t// here's shmock. Neat huh?\n\t\t\t$incrementing_service_mock = $this-\u003eshmock('\\Foo\\Incrementing_Service', function($shmock)\n\t\t\t{\n\t\t\t\t$shmock-\u003eincrement(0)-\u003ereturn_value(1);\n\t\t\t});\n\n\t\t\t$foo = new Foo($incrementing_service_mock);\n\t\t\t$this-\u003eassertEquals(1, $foo-\u003enext_foo(0));\n\t\t}\n\t}\n  ```\n## Installation\n\nShmock can be installed directly from [Packagist](https://packagist.org/packages/box/shmock).\n\n```json\n\"require\": {\n    \"box/shmock\": \"1.0.0.x-dev\"\n}\n```\nAlternatively you can download Shmock.php and Shmockers.php into your test directory and run\n\n```\n  require_once 'Shmock.php';\n```\nPHPUnit should already be on the load path for this to work.\n\n## Type Safety\n\nShmock is typesafe by default and will attempt to tell you when you're using the wrong mocking approach. Shmock will throw errors in cases where:\n\n* You mock a static method as though it were an instance method or vice versa.\n* You mock a private method as though it were protected or public.\n* You mock a method that does not exist _and_ there is no __call / __callStatic magic method provided.\n\nThese checks can be disabled by calling `$mock_object-\u003edisable_strict_method_checking()` inside the shmock closure. We also plan on supporting parameter and return value checking if it complies with yet-to-be-defined PHPDoc conventions.\n\n## Documentation:\n\n[http://box.github.io/shmock/namespaces/Shmock.html](http://box.github.io/shmock/namespaces/Shmock.html)\n\n## Full list of Shmock features:\n  ```php\n  \u003c?php\n  // This code could conceptually be part of a test method from the above Foo_Test class\n\t$inc_service = $this-\u003eshmock('\\Foo\\Incrementing_Service', function($my_class_shmock) // [1]\n\t{\n\t\t$my_class_shmock-\u003eno_args_method(); // [2]\n\t\t$my_class_shmock-\u003etwo_arg_method('param1', 'param2'); // [3]\n\t\t$my_class_shmock-\u003emethod_that_returns_a_number()-\u003ereturn_value(100); // [4]\n\t\t$my_class_shmock-\u003emethod_that_gets_run_twice()-\u003etimes(2); // [5]\n\t\t$my_class_shmock-\u003emethod_that_gets_run_any_times()-\u003eany(); // [6]\n\n\t\t$my_class_shmock-\u003emethod_puts_it_all_together('with', 'args')-\u003etimes(2)-\u003ereturn_value(false);\n\n\t\t$my_class_shmock-\u003emethod_returns_another_mock()-\u003ereturn_shmock('\\Another_Namespace\\Another_Class', function($another_class) // [7]\n\t\t{\n\t\t\t$another_class-\u003eorder_matters(); // [8]\n\t\t\t$another_class-\u003edisable_original_constructor(); // [9a]\n\t\t\t$another_class-\u003eset_constructor_arguments(1, 'Foo'); // [9b]\n\n\t\t\t$another_class-\u003emethod_dies_horribly()-\u003ethrow_exception(new InvalidArgumentException()); // [10]\n\n\t\t\t$another_class-\u003emethod_gets_stubbed(1,2)-\u003ewill(function(PHPUnit_Framework_MockObject_Invocation $invocation)\n\t\t\t{\n\t\t\t\t$a = $invocation-\u003eparameters[0];\n\t\t\t\t$b = $invocation-\u003eparameters[1];\n\t\t\t\treturn $a + $b; // [11]\n\t\t\t});\n\t\t});\n\n\t\t$my_class_shmock-\u003eshmock_class(function($Inc_Service)\n\t\t{\n\t\t\t$Inc_Service-\u003emy_static_method()-\u003eany()-\u003ereturn_value('This was returned inside the mock instance using the static:: prefix'); // [12]\n\t\t});\n\n\t})\n\n\t$another_class = $this-\u003eshmock_class('\\Another_Namespace\\Another_Class', function($Another_Class) // [13]\n\t{\n\t\t$Another_Class-\u003ea_static_method()-\u003ereturn_value(1);\n\t});\n  ```\n\n1. Shmock lets you configure a mock object inside a closure. You work with a proxy object that feels like the real thing.\n2. Invoking a method sets up the expectation that it will be called once.\n3. Invoking a method with arguments causes it to expect those arguments when actually invoked.\n4. You can return values from specific invocations. In the example, the value 100 will be returned when you call the method.\n5. You can specify an expectation for the number of times a method will be called. By default it's expected once.\n6. Or you can specify \"0 or more\" times with any()\n7. You can nest your Shmock invocations, letting you define your mock dependencies elegantly. (If you have a two-way dependency, you can always just `return_value($other_shmock)` and define it somewhere else )\n8. On an object-level you can specify \"order matters\", meaning that the ordering of function invocations should be asserted against as well. Under the hood, this uses PHPUnit's `at(N)` calls automatically\n9. You have some options as far as defining constructor arguments. a) You can opt to disable the original constructor. Normally PHPUnit will run the original constructor. b) You can run the original constructor with the given arguments.\n10. Instead of returning a value, you can throw an exception when a method gets called.\n11. Even more sophisticated, you can execute an arbitrary closure when the function gets called.\n12. If you want to mock static functions, you call `shmock_class` which will give you all the same Shmock semantics as instances (where it makes sense). This is particularly useful when you want to partially mock an object, keeping some of the original behavior, but mocking out static / protected methods that may exist that the method you are testing is dependent on.\n13. You can also mock a class independently of a mock instance.\n\n## Copyright and License\n\nCopyright 2014 Box, Inc. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbox%2Fshmock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbox%2Fshmock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbox%2Fshmock/lists"}