{"record":{"id":"7a88395254c5a0f2","repo":"sebastianbergmann/phpunit","slug":"trying-to-double-property-s-of-class-s-with","errorCode":null,"errorMessage":"Trying to double property \"%s\" of class \"%s\" with doubleProperties(), but it does not exist","messagePattern":"Trying to double property \"(.+?)\" of class \"(.+?)\" with doubleProperties\\(\\), but it does not exist","errorType":"exception","errorClass":"PropertyCannotBeDoubledException","httpStatus":null,"severity":"error","filePath":"src/Framework/MockObject/TestDoubleBuilder.php","lineNumber":125,"sourceCode":"     */\n    public function doubleProperties(array $properties): static\n    {\n        try {\n            $reflector = new ReflectionClass($this->type);\n\n            // @codeCoverageIgnoreStart\n        } catch (\\ReflectionException $e) {\n            throw new ReflectionException(\n                $e->getMessage(),\n                $e->getCode(),\n                $e,\n            );\n            // @codeCoverageIgnoreEnd\n        }\n\n        foreach ($properties as $propertyName) {\n            if (!$reflector->hasProperty($propertyName)) {\n                throw new PropertyCannotBeDoubledException($this->type, $propertyName, 'it does not exist');\n            }\n\n            $property = $reflector->getProperty($propertyName);\n\n            if (!$property->isPublic()) {\n                throw new PropertyCannotBeDoubledException($this->type, $propertyName, 'it is not public');\n            }\n\n            if ($property->isStatic()) {\n                throw new PropertyCannotBeDoubledException($this->type, $propertyName, 'it is static');\n            }\n\n            if ($property->isReadOnly()) {\n                throw new PropertyCannotBeDoubledException($this->type, $propertyName, 'it is readonly');\n            }\n\n            if ($property->isFinal()) {\n                throw new PropertyCannotBeDoubledException($this->type, $propertyName, 'it is final');","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Framework/MockObject/TestDoubleBuilder.php#L107-L143","documentation":"doubleProperties() requires every passed name to be an actually declared property of the target class — ReflectionClass::hasProperty() must find it. If the name is unknown (typo, undeclared dynamic property, wrong class), PHPUnit throws PropertyCannotBeDoubledException with the reason 'it does not exist' (TestDoubleBuilder.php:123-126). Unlike addMethods() for methods, there is no API for adding properties to a test double, so the name must be real.","triggerScenarios":"$this->getMockBuilder(User::class)->doubleProperties(['emial']) where User declares $email but not $emial: hasProperty('emial') returns false and PropertyCannotBeDoubledException is thrown with 'it does not exist'. Also when the property exists only as a docblock @property annotation, is a magic __get/__set shadow, or lives on a different class than the one being mocked.","commonSituations":"Typos or renames of entity properties after a migration/refactor; assuming a parent-class or trait property exists on the mocked subclass when it actually does not; trying to double runtime-assigned dynamic properties (not declared in the class); @property-read annotations on DTOs mistaken for real declarations; copying doubleProperties() lists between related classes.","solutions":["Verify against the class: ReflectionProperty check or var_dump of get_class_vars(User::class) to get the exact declared name, then correct the string in doubleProperties().","If the property was renamed upstream, update the test to the new name.","Remove the name from the list if the property does not need hook doubling — undeclared/dynamic properties cannot be doubled at all.","If the property is declared on a parent/trait, mock the class that actually declares it, or double it there and extend the mock."],"exampleFix":"// before\n$mock = $this->getMockBuilder(User::class)\n             ->doubleProperties(['emial'])  // typo: declared property is $email\n             ->getMock();\n\n// after\n$mock = $this->getMockBuilder(User::class)\n             ->doubleProperties(['email'])\n             ->getMock();","handlingStrategy":"validation","validationCode":"// Verify declared properties before doubling:\n$reflector = new ReflectionClass(User::class);\nforeach (['email', 'name'] as $prop) {\n    if (!$reflector->hasProperty($prop)) {\n        self::fail(\"User::\\${$prop} is not a declared property (docblock @property cannot be doubled)\");\n    }\n}\n$mock = $this->getMockBuilder(User::class)->doubleProperties(['email', 'name'])->getMock();","typeGuard":"/** @param list<non-empty-string> $props @return list<non-empty-string> */\nfunction declaredProperties(string $class, array $props): array\n{\n    $r = new ReflectionClass($class);\n    return array_values(array_filter($props, static fn (string $p): bool => $r->hasProperty($p)));\n}","tryCatchPattern":"use PHPUnit\\Framework\\MockObject\\PropertyCannotBeDoubledException;\n\ntry {\n    $builder->doubleProperties($names);\n} catch (PropertyCannotBeDoubledException $e) {\n    self::fail('Property cannot be doubled: ' . $e->getMessage());\n}","preventionTips":["Take property names from the class source (or Reflection), not from docblock @property annotations.","Update doubleProperties() lists in the same commit that renames entity fields.","Remember there is no addProperties() counterpart — undeclared/dynamic properties cannot be doubled at all.","When a property lives on a parent class, double it against the declaring class."],"tags":["phpunit","mockobject","property-doubling","unknown-property","reflection"],"backgroundTag":"mocking-undeclared-member","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}