{"id":13562492,"url":"https://github.com/paratestphp/paratest","last_synced_at":"2026-02-25T16:08:44.280Z","repository":{"id":5102636,"uuid":"6266214","full_name":"paratestphp/paratest","owner":"paratestphp","description":":computer: Parallel testing for PHPUnit","archived":false,"fork":false,"pushed_at":"2026-02-06T10:54:43.000Z","size":2966,"stargazers_count":2444,"open_issues_count":3,"forks_count":249,"subscribers_count":35,"default_branch":"7.x","last_synced_at":"2026-02-06T17:53:41.434Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"guillaumepotier/Parsley.js","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paratestphp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["Slamdunk"],"custom":"https://paypal.me/filippotessarotto"}},"created_at":"2012-10-17T18:22:52.000Z","updated_at":"2026-02-06T10:53:32.000Z","dependencies_parsed_at":"2023-07-06T05:56:10.362Z","dependency_job_id":"62378949-d183-4eaa-81f4-53245e8fee44","html_url":"https://github.com/paratestphp/paratest","commit_stats":{"total_commits":1090,"total_committers":137,"mean_commits":7.956204379562044,"dds":0.7908256880733945,"last_synced_commit":"ec931fb521d6e8b7512c51e4012cc143caa47ba0"},"previous_names":["brianium/paratest"],"tags_count":170,"template":false,"template_full_name":null,"purl":"pkg:github/paratestphp/paratest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paratestphp%2Fparatest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paratestphp%2Fparatest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paratestphp%2Fparatest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paratestphp%2Fparatest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paratestphp","download_url":"https://codeload.github.com/paratestphp/paratest/tar.gz/refs/heads/7.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paratestphp%2Fparatest/sbom","scorecard":{"id":720327,"data":{"date":"2025-08-11","repo":{"name":"github.com/paratestphp/paratest","commit":"7fbf804253b81bf526c381e3fb915c7a5f6d1562"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.5,"checks":[{"name":"Maintained","score":10,"reason":"26 commit(s) and 5 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/25 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":1,"reason":"branch protection is not maximal on development and all release branches","details":["Warn: branch protection not enabled for branch '7.4.x'","Warn: branch protection not enabled for branch '7.8.x'","Info: 'allow deletion' disabled on branch '7.x'","Info: 'force pushes' disabled on branch '7.x'","Info: 'branch protection settings apply to administrators' is required to merge on branch '7.x'","Warn: 'stale review dismissal' is disabled on branch '7.x'","Warn: branch '7.x' does not require approvers","Warn: codeowners review is not required on branch '7.x'","Warn: 'last push approval' is disabled on branch '7.x'","Warn: no status checks found to merge onto branch '7.x'","Info: PRs are required in order to make changes on branch '7.x'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T11:05:43.415Z","repository_id":5102636,"created_at":"2025-08-22T11:05:43.415Z","updated_at":"2025-08-22T11:05:43.415Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29829423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T15:41:19.027Z","status":"ssl_error","status_checked_at":"2026-02-25T15:40:47.150Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-08-01T13:01:09.190Z","updated_at":"2026-02-25T16:08:44.248Z","avatar_url":"https://github.com/paratestphp.png","language":"PHP","readme":"ParaTest\n========\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/brianium/paratest.svg)](https://packagist.org/packages/brianium/paratest)\n[![Downloads](https://img.shields.io/packagist/dt/brianium/paratest.svg)](https://packagist.org/packages/brianium/paratest)\n[![Integrate](https://github.com/paratestphp/paratest/actions/workflows/ci.yml/badge.svg?branch=7.x)](https://github.com/paratestphp/paratest/actions)\n[![Infection MSI](https://img.shields.io/endpoint?style=flat\u0026url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fparatestphp%2Fparatest%2F7.x)](https://dashboard.stryker-mutator.io/reports/github.com/paratestphp/paratest/7.x)\n\nThe objective of ParaTest is to support parallel testing in PHPUnit. Provided you have well-written PHPUnit tests, you can drop `paratest` in your project and\nstart using it with no additional bootstrap or configurations!\n\nBenefits:\n\n* Zero configuration. After the installation, run with `vendor/bin/paratest` to parallelize by TestCase or `vendor/bin/paratest --functional` to parallelize by Test. That's it!\n* Code Coverage report combining. Run your tests in N parallel processes and all the code coverage output will be combined into one report.\n\n# Installation\n\nTo install with composer run the following command:\n\n    composer require --dev brianium/paratest\n    \n# Versions\n\nOnly the latest version of PHPUnit is supported, and thus only the latest version of ParaTest is actively maintained.\n\nThis is because of the following reasons:\n\n1. To reduce bugs, code duplication and incompatibilities with PHPUnit, from version 5 ParaTest heavily relies on PHPUnit `@internal` classes\n1. The fast pace both PHP and PHPUnit have taken recently adds too much maintenance burden, which we can only afford for the latest versions to stay up-to-date\n\n# Usage\n\nAfter installation, the binary can be found at `vendor/bin/paratest`. Run it\nwith `--help` option to see a complete list of the available options.\n\n## Test token\n\nThe `TEST_TOKEN` environment variable is guaranteed to have a value that is different\nfrom every other currently running test. This is useful to e.g. use a different database\nfor each test:\n\n```php\nif (getenv('TEST_TOKEN') !== false) {  // Using ParaTest\n    $dbname = 'testdb_' . getenv('TEST_TOKEN');\n} else {\n    $dbname = 'testdb';\n}\n```\n\nA `UNIQUE_TEST_TOKEN` environment variable is also available and guaranteed to have a value that is unique both\nper run and per process.\n\n## Code coverage\n\nThe cache is always warmed up by ParaTest before executing the test suite.\n\n### PCOV\n\nIf you have installed `pcov` but need to enable it only while running tests, you have to pass thru the needed PHP binary\noption:\n\n```\nphp -d pcov.enabled=1 vendor/bin/paratest --passthru-php=\"'-d' 'pcov.enabled=1'\"\n```\n\n### xDebug\n\nIf you have `xDebug` installed, activating it by the environment variable is enough to have it running even in the subprocesses:\n\n```\nXDEBUG_MODE=coverage vendor/bin/paratest\n```\n\n## Initial setup for all tests\n\nBecause ParaTest runs multiple processes in parallel, each with their own instance of the PHP interpreter,\ntechniques used to perform an initialization step exactly once for each test work different from PHPUnit.\nThe following pattern will not work as expected - run the initialization exactly once - and instead run the\ninitialization once per process:\n\n```php\nprivate static bool $initialized = false;\n\npublic function setUp(): void\n{\n    if (! self::$initialized) {\n         self::initialize();\n         self::$initialized = true;\n    }\n}\n```\n\nThis is because static variables persist during the execution of a single process.\nIn parallel testing each process has a separate instance of `$initialized`.\nYou can use the following pattern to ensure your initialization runs exactly once for the entire test invocation:\n\n```php\nstatic bool $initialized = false;\n\npublic function setUp(): void\n{\n    if (! self::$initialized) {\n        // We utilize the filesystem as shared mutable state to coordinate between processes\n        touch('/tmp/test-initialization-lock-file');\n        $lockFile = fopen('/tmp/test-initialization-lock-file', 'r');\n\n        // Attempt to get an exclusive lock - first process wins\n        if (flock($lockFile, LOCK_EX | LOCK_NB)) {\n            // Since we are the single process that has an exclusive lock, we run the initialization\n            self::initialize();\n        } else {\n            // If no exclusive lock is available, block until the first process is done with initialization\n            flock($lockFile, LOCK_SH);\n        }\n\n        self::$initialized = true;\n    }\n}\n```\n\n## Troubleshooting\n\nIf you run into problems with `paratest`, try to get more information about the issue by enabling debug output via\n`--verbose`.\n\nWhen a sub-process fails, the originating command is given in the output and can then be copy-pasted in the terminal\nto be run and debugged. All internal commands run with `--printer [...]\\NullPhpunitPrinter` which silence the original\nPHPUnit output: during a debugging run remove that option to restore the output and see what PHPUnit is doing.\n\n## Caveats\n\n1. Constants, static methods, static variables and everything exposed by test classes consumed by other test classes\n(including Reflection) are not supported. This is due to a limitation of the current implementation of `WrapperRunner`\nand how PHPUnit searches for classes. The fix is to put shared code into classes which are not tests _themselves_.\n\n## Integration with PHPStorm\n\nParaTest provides a dedicated binary to work with PHPStorm; follow these steps to have ParaTest working within it:\n\n1. Be sure you have PHPUnit already configured in PHPStorm: https://www.jetbrains.com/help/phpstorm/using-phpunit-framework.html#php_test_frameworks_phpunit_integrate\n2. Go to `Run` -\u003e `Edit configurations...`\n3. Select `Add new Configuration`, select the `PHPUnit` type and name it `ParaTest`\n4. In the `Command Line` -\u003e `Interpreter options` add `./vendor/bin/paratest_for_phpstorm`\n5. Any additional ParaTest options you want to pass to ParaTest should go within the `Test runner` -\u003e `Test runner options` section\n\nYou should now have a `ParaTest` run within your configurations list.\nIt should natively work with the `Rerun failed tests` and `Toggle auto-test` buttons of the `Run` overlay.\n\n### Run with Coverage\n\nCoverage with one of the [available coverage engines](#code-coverage) must already be [configured in PHPStorm](https://www.jetbrains.com/help/phpstorm/code-coverage.html) \nand working when running tests sequentially in order for the helper binary to correctly handle code coverage\n\n# For Contributors: testing ParaTest itself\n\nBefore creating a Pull Request be sure to run all the necessary checks with `make` command.\n","funding_links":["https://github.com/sponsors/Slamdunk","https://paypal.me/filippotessarotto"],"categories":["PHP","Table of Contents","类库","目录"],"sub_categories":["Testing","测试框架","测试 Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparatestphp%2Fparatest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fparatestphp%2Fparatest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparatestphp%2Fparatest/lists"}