{"record":{"id":"fd03b0437f1362f0","repo":"pestphp/pest","slug":"test-closures-may-not-be-static-please-remove-the","errorCode":null,"errorMessage":"Test closures may not be static. Please remove the [static] keyword from the test [%s] in [%s].","messagePattern":"Test closures may not be static\\. Please remove the \\[static\\] keyword from the test \\[(.+?)\\] in \\[(.+?)\\]\\.","errorType":"exception","errorClass":"TestClosureMustNotBeStatic","httpStatus":null,"severity":"error","filePath":"src/Factories/TestCaseFactory.php","lineNumber":195,"sourceCode":"        }\n    }\n\n    public function addMethod(TestCaseMethodFactory $method): void\n    {\n        if ($method->description === null) {\n            throw new TestDescriptionMissing($method->filename);\n        }\n\n        if (array_key_exists($method->description, $this->methods)) {\n            throw new TestAlreadyExist($method->filename, $method->description);\n        }\n\n        if (\n            $method->closure instanceof \\Closure &&\n            new \\ReflectionFunction($method->closure)->isStatic()\n        ) {\n\n            throw new TestClosureMustNotBeStatic($method);\n        }\n\n        if (! $method->receivesArguments()) {\n            if (! $method->closure instanceof \\Closure) {\n                throw ShouldNotHappen::fromMessage('The test closure may not be empty.');\n            }\n\n            $arguments = Reflection::getFunctionArguments($method->closure);\n\n            if ($arguments !== []) {\n                throw new DatasetMissing($method->filename, $method->description, $arguments);\n            }\n        }\n\n        $this->methods[$method->description] = $method;\n    }\n\n    public function hasMethod(string $methodName): bool","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/pestphp/pest/blob/1af74a215c3a89bc7a44fc6f9322e369eaad2423/src/Factories/TestCaseFactory.php#L177-L213","documentation":"Pest binds test closures to the test case instance so $this (datasets via constructor promotion, $this->... helpers, beforeEach state) works. Static closures have no bound scope, so TestCaseFactory::addMethod rejects them with TestClosureMustNotBeStatic, naming the test and file. It is a load-time error: the test never runs.","triggerScenarios":"test('works', static function () { ... }); it('...', static fn () => ...); refactoring service code into tests and leaving the static keyword; IDE auto-import or a coding standard (static analysis fixers) adding static to closures for performance, catching test closures too.","commonSituations":"Running php-cs-fixer / Pint / a 'static closures' fixer rule over the tests directory; copying static closures from production code into tests; developers optimizing closure instantiation without realizing Pest relies on binding ($this->set(...), $this->name, etc.).","solutions":["Remove the static keyword from the test closure (the message names the test and file).","Configure your fixer to exclude tests: e.g., in Pint/php-cs-fixer, disable the static_lambda or native_function_invocation-style rules for tests/ or set a finder that skips Pest files.","If the closure must stay reusable, move the logic to a named helper and call it from a non-static test closure."],"exampleFix":"// before\ntest('calculates total', static function () {\n    expect(total(1, 2))->toBe(3);\n});\n// after\ntest('calculates total', function () {\n    expect(total(1, 2))->toBe(3);\n});","handlingStrategy":"validation","validationCode":"// php-cs-fixer: exclude tests from static-lambda fixes (pest.hjson or .php-cs-fixer.php)\n->setRules([\n    'static_lambda' => false,\n])\n->in([__DIR__.'/app', __DIR__.'/src']) // tests/ deliberately not included","typeGuard":"function isStaticClosure(Closure $closure): bool\n{\n    return (new ReflectionFunction($closure))->isStatic();\n}\n\n// usage in generators:\nif (isStaticClosure($closure)) {\n    $closure = Closure::bind($closure, null, $scope) ?? $closure; // or reject: static not allowed\n}","tryCatchPattern":null,"preventionTips":["Never write static closures in test files — Pest binds them to the test case for $this support.","Scope static-lambda/style fixers to app code, not tests/.","If a helper returns closures for tests, assert they are non-static in a meta-test."],"tags":["pest","static-closure","binding","load-time","cs-fixer"],"backgroundTag":"static-closure-not-allowed","analyzedSha":"1af74a215c3a89bc7a44fc6f9322e369eaad2423","analyzedAt":"2026-08-21T02:16:38.132Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}