{"id":15014443,"url":"https://github.com/codeception/specify","last_synced_at":"2025-04-12T21:33:46.778Z","repository":{"id":9699640,"uuid":"11649551","full_name":"Codeception/Specify","owner":"Codeception","description":"BDD style code blocks for PHPUnit / Codeception","archived":false,"fork":false,"pushed_at":"2022-06-08T07:46:49.000Z","size":1203,"stargazers_count":156,"open_issues_count":6,"forks_count":23,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-04T01:08:53.849Z","etag":null,"topics":["bdd-style","codeception","php","phpunit"],"latest_commit_sha":null,"homepage":null,"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}},"created_at":"2013-07-25T01:52:27.000Z","updated_at":"2025-03-04T10:01:00.000Z","dependencies_parsed_at":"2022-07-07T22:48:54.086Z","dependency_job_id":null,"html_url":"https://github.com/Codeception/Specify","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FSpecify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FSpecify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FSpecify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Codeception%2FSpecify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Codeception","download_url":"https://codeload.github.com/Codeception/Specify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248636808,"owners_count":21137527,"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":["bdd-style","codeception","php","phpunit"],"created_at":"2024-09-24T19:45:38.478Z","updated_at":"2025-04-12T21:33:46.746Z","avatar_url":"https://github.com/Codeception.png","language":"PHP","readme":"Specify\n=======\n\nBDD style code blocks for [PHPUnit][1] or [Codeception][2]\n\n[![Latest Stable Version](https://poser.pugx.org/codeception/specify/v/stable)](https://packagist.org/packages/codeception/specify)\n[![Total Downloads](https://poser.pugx.org/codeception/specify/downloads)](https://packagist.org/packages/codeception/specify)\n[![Latest Unstable Version](https://poser.pugx.org/codeception/specify/v/unstable)](https://packagist.org/packages/codeception/specify)\n[![License](https://poser.pugx.org/codeception/specify/license)](https://packagist.org/packages/codeception/specify)\n[![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)\n\nSpecify allows you to write your tests in more readable BDD style, the same way you might have experienced with [Jasmine][3].\nInspired by MiniTest of Ruby now you combine BDD and classical TDD style in one test.\n\n## Installation\n\n*Requires PHP \u003e= 7.4*\n\n* Install with Composer:\n\n```\ncomposer require codeception/specify --dev\n```\n\n* Include `Codeception\\Specify` trait in your tests.\n\n\n## Usage\n\nSpecify `$this-\u003especify` method to add isolated test blocks for your PHPUnit tests! \n\n```php\npublic function testValidation()\n{\n    $this-\u003eassertInstanceOf('Model', $this-\u003euser);\n\n    $this-\u003especify('username is required', function() {\n        $this-\u003euser-\u003eusername = null;\n        $this-\u003eassertFalse($this-\u003euser-\u003evalidate(['username']));\n    });\n\n    $this-\u003especify('username is too long', function() {\n        $this-\u003euser-\u003eusername = 'toolooooongnaaaaaaameeee';\n        $this-\u003eassertFalse($this-\u003euser-\u003evalidate(['username']));\n    });\n}\n```\n\n### BDD Example\n\nSpecify supports `describe-it` and `describe-should` BDD syntax inside PHPUnit\n\n```php\npublic function testValidation()\n{\n    $this-\u003edescribe('user', function () {\n        $this-\u003eit('should have a name', function() {\n            $this-\u003euser-\u003eusername = null;\n            $this-\u003eassertFalse($this-\u003euser-\u003evalidate(['username']));\n        });\n    });\n\n    // you can use chained methods for better readability:\n    $this-\u003edescribe('user')\n        -\u003eshould('be ok with valid name', function() {\n            $this-\u003euser-\u003eusername = 'davert';\n            $this-\u003eassertTrue($this-\u003euser-\u003evalidate(['username']));\n        })\n        -\u003eshouldNot('have long name', function() {\n            $this-\u003euser-\u003eusername = 'toolooooongnaaaaaaameeee';\n            $this-\u003eassertFalse($this-\u003euser-\u003evalidate(['username']));\n        })\n        // empty codeblocks are marked as Incomplete tests\n        -\u003eit('should be ok with valid name') \n    ;\n}\n```\n\n\n### Specify + Verify Example\n\nUse [Codeception/Verify][4] for simpler assertions:\n\n```php\npublic function testValidation()\n{\n    $this-\u003especify('username is too long', function() {\n        $this-\u003euser-\u003eusername = 'toolooooongnaaaaaaameeee';\n        expect_not($this-\u003euser-\u003evalidate(['username']));\n    });\n\n    $this-\u003especify('username is ok', function() {\n        $this-\u003euser-\u003eusername = 'davert';\n        expect_that($this-\u003euser-\u003evalidate(['username']));\n    });\n}\n```\n\n## Use Case\n\nThis tiny library makes your tests readable by organizing them in nested code blocks.\nThis allows to combine similar tests into one but put them inside nested sections.\n\nThis is very similar to BDD syntax as in RSpec or Mocha but works inside PHPUnit:\n\n```php\n\u003c?php\n\nclass UserTest extends PHPUnit\\Framework\\TestCase \n{\n    use Codeception\\Specify;\n\n    /** @specify */\n    protected $user; // is cloned inside specify blocks\n\n    public function setUp(): void\n    {\n        $this-\u003euser = new User;\n    }\n\n    public function testValidation()\n    {\n        $this-\u003euser-\u003ename = 'davert';\n        $this-\u003especify('i can change my name', function() {\n           $this-\u003euser-\u003ename = 'jon';\n           $this-\u003eassertEquals('jon', $this-\u003euser-\u003ename);\n        });\n        // user name is 'davert' again\n        $this-\u003eassertEquals('davert', $this-\u003euser-\u003ename);\n    }\n}\n```\n\nEach code block is isolated. This means call to `$this-\u003especify` does not change values of properties of a test class.\nIsolated properties should be marked with `@specify` annotation.\n\nFailure in `specify` block won't get your test stopped.\n\n```php\n\u003c?php\n$this-\u003especify('failing but test goes on', function() {\n\t$this-\u003efail('bye');\n});\n$this-\u003eassertTrue(true);\n\n// Assertions: 2, Failures: 1\n?\u003e\n```\n\nIf a test fails you will see specification text in the result.\n\n## Isolation\n\nIsolation is achieved by **cloning object properties** for each specify block.\nOnly properties marked with `@specify` annotation are cloned. \n\n```php\n/** @specify */\nprotected $user; // cloning\n\n/** \n * @specify \n **/\nprotected $user; // cloning\n\nprotected $repository; // not cloning\n```\n\nObjects are cloned using deep cloning method. \n\n**If object cloning affects performance, consider turning the clonning off**.\n\n**Mocks are isolated** by default. \n\nA mock defined inside a specify block won't be executed inside an outer test,\nand mock from outer test won't be triggered inside codeblock.\n\n```php\n\u003c?php\n$config = $this-\u003ecreateMock(Config::class);\n$config-\u003eexpects($this-\u003eonce())-\u003emethod('init');\n\n$config-\u003einit();\n// success: $config-\u003einit() was executed\n\n$this-\u003especify('this should not fail', function () {\n    $config = $this-\u003ecreateMock(Config::class);\n    $config-\u003eexpects($this-\u003enever())-\u003emethod('init')-\u003ewillReturn(null);\n    // success: $config-\u003einit() is never executed \n});\n```\n\n## Examples: DataProviders alternative\n\n```php\n\u003c?php\n$this-\u003especify('should calculate square numbers', function($number, $square) {\n\t$this-\u003eassertEquals($square, $number*$number);\n}, ['examples' =\u003e [\n\t\t[2,4],\n\t\t[3,9]\n]]);\n```\n\nYou can also use DataProvider functions in `examples` param.\n\n```php\n\u003c?php\n$this-\u003especify('should calculate square numbers', function($number, $square) {\n\t$this-\u003eassertEquals($square, $number*$number);\n}, ['examples' =\u003e $this-\u003eprovider()]);\n```\n\nCan also be used with real data providers:\n\n```php\n\u003c?php\n/**\n * @dataProvider someData\n */\npublic function testExamplesAndDataProvider($param)\n{\n    $this-\u003especify('should assert data provider', function ($example) use ($param) {\n        $this-\u003eassertGreaterThanOrEqual(5, $param + $example);\n    }, ['examples' =\u003e [[4], [7], [5]]]);\n}\n\npublic function someData()\n{\n    return [[1], [2]];\n}\n```\n\n## Before/After\n\nThere are also before and after callbacks, which act as setUp/tearDown but for specify.\n\n```php\n\u003c?php\n$this-\u003ebeforeSpecify(function() {\n\t// prepare something;\t\n});\n$this-\u003eafterSpecify(function() {\n\t// reset something\n});\n$this-\u003ecleanSpecify(); // removes before/after callbacks\n?\u003e\n```\n\n## API\n\nAvailable methods:\n\n```php\n// Starts a specify code block:\n$this-\u003especify(string $thing, callable $code = null, $examples = [])\n\n// Starts a describe code block. Same as 'specify' but expects chained 'it' or 'should' methods.\n$this-\u003edescribe(string $feature, callable $code = null)\n\n// Starts a code block. If 'code' is null, marks test as incomplete.\n$this-\u003eit(string $spec, callable $code = null, $examples = [])\n$this-\u003eits(string $spec, callable $code = null, $examples = [])\n\n// Starts a code block. Same as 'it' but prepends 'should' or 'should not' into description.\n$this-\u003eshould(string $behavior, callable $code = null, $examples = [])\n$this-\u003eshouldNot(string $behavior, callable $code = null, $examples = [])\n```\n\n## Printer Options\n\nFor PHPUnit, add `Codeception\\Specify\\ResultPrinter` printer into `phpunit.xml`\n\n```xml\n\u003cphpunit colors=\"true\" printerClass=\"Codeception\\Specify\\ResultPrinter\"\u003e\n\u003c/phpunit\u003e\n```\n\n## Recommended\n\n* Use [Codeception/AssertThrows][5] for exception assertions\n* Use [Codeception/DomainAssert][6] for verbose domain logic assertions\n* Combine this with [Codeception/Verify][4] library, to get BDD style assertions.\n\nLicense: [MIT.][7]\n\n[1]: https://phpunit.de/\n[2]: https://codeception.com/\n[3]: https://jasmine.github.io/\n[4]: https://github.com/Codeception/Verify\n[5]: https://github.com/Codeception/AssertThrows\n[6]: https://github.com/Codeception/DomainAssert\n[7]: /LICENSE\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeception%2Fspecify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodeception%2Fspecify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeception%2Fspecify/lists"}