{"id":18761098,"url":"https://github.com/cdn77/testutils","last_synced_at":"2025-08-23T15:41:00.495Z","repository":{"id":35077350,"uuid":"204034869","full_name":"cdn77/TestUtils","owner":"cdn77","description":"Test Utils for PHP to facilitate writing tests","archived":false,"fork":false,"pushed_at":"2025-08-11T14:53:41.000Z","size":158,"stargazers_count":2,"open_issues_count":3,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-17T10:55:32.476Z","etag":null,"topics":["hacktoberfest","php","phpunit","testing","testing-tools","testutils"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cdn77.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2019-08-23T16:34:25.000Z","updated_at":"2025-07-28T23:43:30.000Z","dependencies_parsed_at":"2023-02-18T08:20:17.762Z","dependency_job_id":"e4cdcad7-6c2c-4373-ae27-08dbb49cc44e","html_url":"https://github.com/cdn77/TestUtils","commit_stats":{"total_commits":85,"total_committers":3,"mean_commits":"28.333333333333332","dds":"0.22352941176470587","last_synced_commit":"a09866943c05fa80ec094d2d9ab7af32591f5d56"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/cdn77/TestUtils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdn77%2FTestUtils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdn77%2FTestUtils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdn77%2FTestUtils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdn77%2FTestUtils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cdn77","download_url":"https://codeload.github.com/cdn77/TestUtils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdn77%2FTestUtils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271754951,"owners_count":24815328,"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","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["hacktoberfest","php","phpunit","testing","testing-tools","testutils"],"created_at":"2024-11-07T18:14:57.915Z","updated_at":"2025-08-23T15:41:00.444Z","avatar_url":"https://github.com/cdn77.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cdn77 TestUtils\n\n[![GitHub Actions][GA Image]][GA Link]\n[![Code Coverage][Coverage Image]][CodeCov Link]\n[![Downloads][Downloads Image]][Packagist Link]\n[![Packagist][Packagist Image]][Packagist Link]\n[![Infection MSI][Infection Image]][Infection Link]\n\n## Contents\n\n- [Installation](#installation)\n- [Features](#features)\n  - [Stub](#stub)\n  - [Test Checks](#test-checks)\n\n## Installation\n\n* Require this project as composer dev dependency:\n\n```\ncomposer require --dev cdn77/test-utils\n```\n\n## Features\n\n### Stub\n\nFactory to create object through Reflection in order to bypass the constructor.\n\n----------------\n\n```php\n\u003c?php\n\nclass MyEntity \n{\n    /** @var string */\n    private $property1;\n\n    /** @var string */\n    private $property2;\n\n    public function __construct(string $property1, string $property2) \n    {\n        $this-\u003eproperty1 = $property1;\n        $this-\u003eproperty2 = $property2;\n    }\n\n    public function salute() : string \n    {\n        return sprintf('Hello %s!', $this-\u003eproperty2);\n    }\n}\n```\n\nWhen testing method `salute()`, you only need the tested class to have `property2` set, you don't want to worry about `property1`. \nTherefore in your test you can initialize `MyEntity` using `Stub::create()` like this:\n\n```php\n$myEntity = Stub::create(MyEntity::class, ['property2' =\u003e 'world']);\n\nself::assertSame('Hello world!', $myEntity-\u003esalute());\n```\n\nIt comes handy when class constructor has more arguments and most of them are not required for your test.\n\nIt is possible to extend stubs:\n\n```php\n$myEntity = Stub::create(MyEntity::class, ['property2' =\u003e 'world']);\n$myEntity = Stub::extends($myEntity, ['property1' =\u003e 'value']);\n\n// property 1 and 2 are set now\nself::assertSame('Hello world!', $myEntity-\u003esalute());\n```\n\n### Test Checks\n\nTest Checks are used to assert that tests comply with your suite's standards (are final, extend correct TestCaseBase etc.)\n\nTo run them, e.g. create a test case like in the following example: \n\n```php\n\u003c?php\n\nuse Cdn77\\TestUtils\\TestCheck\\TestCheck;\nuse PHPUnit\\Framework\\TestCase;\nuse PHPUnit\\Framework\\Attributes\\CoversNothing;\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n #[CoversNothing]\n #[Group('integration')]\nfinal class SuiteComplianceTest extends TestCaseBase\n{\n    /** @dataProvider providerChecks */\n    public function testChecks(TestCheck $check) : void\n    {\n        $check-\u003erun($this);\n    }\n\n    /** @return Generator\u003cstring, array{callable(self): TestCheck}\u003e */\n    public static function providerChecks() : Generator\n    {\n        $testDir = ROOT_PROJECT_DIR . '/tests';\n        $testFilePathNames = \\Symfony\\Component\\Finder\\Finder::create()\n            -\u003ein($testDir)\n            -\u003efiles()\n            -\u003ename('*Test.php');\n\n        yield 'Every test has group' =\u003e [\n            new EveryTestHasGroup($testFilePathNames),\n        ];\n        \n        ...\n    }\n}\n```\n\n### Every test has group\n\nAsserts that all tests have a `#[Group('x')]` attribute \n\n:x:\n```php\nfinal class FooTest extends TestCase\n```\n\n:heavy_check_mark:\n```php\nuse PHPUnit\\Framework\\Attributes\\Group;\n\n #[Group('unit')]\nfinal class FooTest extends TestCase\n```\n\nConfigured in test provider as\n\n```php\nyield 'Every test has group' =\u003e [\n    new EveryTestHasGroup($testFiles),\n];\n```\n\n### Every test has same namespace as covered class\n\nAsserts that all test share same namespace with class they're testing.  \nConsider src namespace `Ns` and test namespace `Ns/Tests` then for test `Ns/Tests/UnitTest` must exist class `Ns/Unit`. \n\nYou can use `#[CoversClass]` attribute to link test with tested class.  \nUse `#[CoversNothing]` attribute to skip this check.\n\nDon't forget to enable `requireCoverageMetadata=\"true\"` in phpunit config file.\n\n```php\nnamespace Ns;\n\nfinal class Unit {} \n```\n\n:x:\n```php\nnamespace Ns\\Tests;\n\nfinal class NonexistentUnitTest extends TestCase {}\n```\n\n```php\nnamespace Ns\\Tests\\Sub;\n\nfinal class UnitTest extends TestCase {}\n```\n\n:heavy_check_mark:\n```php\nnamespace Ns\\Tests;\n\nfinal class UnitTest extends TestCase {}\n```\n\n```php\nnamespace Ns\\Tests\\Sub;\n\nuse PHPUnit\\Framework\\Attributes\\CoversClass;\n\n #[CoversClass('\\Ns\\Unit')]\nfinal class UnitTest extends TestCase {}\n```\n\nConfigured in test provider as\n\n```php\nyield 'Every test has same namespace as tested class' =\u003e [\n    new EveryTestHasSameNamespaceAsCoveredClass($testFiles),\n];\n```\n\n### Every test inherits from testcase base class\n\nConsider you have a base for all tests and want each of them extend it.\n\n```php\nabstract class TestCaseBase extends \\PHPUnit\\Framework\\TestCase {}\n```\n\n:x:\n```php\nfinal class FooTest extends \\PHPUnit\\Framework\\TestCase\n```\n\n:heavy_check_mark:\n```php\nfinal class FooTest extends TestCaseBase\n```\n\nConfigured in test provider as\n\n```php\nyield 'Every test inherits from TestCase Base Class' =\u003e [\n    new EveryTestInheritsFromTestCaseBaseClass(\n        $testFiles,\n        TestCaseBase::class\n    ),\n];\n```\n\n### Every test is final\n\nAsserts all tests are final so they cannot be extended\n\n:x:\n```php\nclass FooTest extends TestCase\n```\n\n:heavy_check_mark:\n```php\nfinal class FooTest extends TestCase\n```\n\nConfigured in test provider as\n\n```php\nyield 'Every test is final' =\u003e [\n    new EveryTestIsFinal($testFiles),\n];\n```\n\n[GA Image]: https://github.com/cdn77/TestUtils/workflows/CI/badge.svg\n\n[GA Link]: https://github.com/cdn77/TestUtils/actions?query=workflow%3A%22CI%22+branch%3Amaster\n\n[Coverage Image]: https://codecov.io/gh/cdn77/TestUtils/branch/master/graph/badge.svg\n\n[CodeCov Link]: https://codecov.io/gh/cdn77/TestUtils/branch/master\n\n[Downloads Image]: https://poser.pugx.org/cdn77/test-utils/d/total.svg\n\n[Packagist Image]: https://poser.pugx.org/cdn77/test-utils/v/stable.svg\n\n[Packagist Link]: https://packagist.org/packages/simpod/test-utils\n\n[Infection Image]: https://img.shields.io/endpoint?style=flat\u0026url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fcdn77%2FTestUtils%2Fmaster\n\n[Infection Link]: https://dashboard.stryker-mutator.io/reports/github.com/cdn77/TestUtils/master\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdn77%2Ftestutils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcdn77%2Ftestutils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdn77%2Ftestutils/lists"}