├── .gitattributes ├── .gitignore ├── Partial ├── Exception │ ├── InvalidNodeNavigation.php │ ├── ChildIsNotAnArrayNode.php │ └── UndefinedChildNode.php ├── PartialProcessor.php └── PartialNode.php ├── LICENSE ├── composer.json ├── PhpUnit ├── ProcessedConfigurationEqualsConstraint.php ├── ConfigurationValuesAreValidConstraint.php ├── AbstractConfigurationConstraint.php ├── ConfigurationValuesAreInvalidConstraint.php └── ConfigurationTestCaseTrait.php ├── CHANGELOG.md ├── .github └── workflows │ └── ci.yaml └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | /Tests export-ignore 2 | /phpunit.xml.dist export-ignore 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .phpunit.result.cache 2 | composer.lock 3 | phpunit.xml 4 | vendor 5 | -------------------------------------------------------------------------------- /Partial/Exception/InvalidNodeNavigation.php: -------------------------------------------------------------------------------- 1 | getPath() 16 | ) 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Partial/Exception/UndefinedChildNode.php: -------------------------------------------------------------------------------- 1 | getPath() 16 | ) 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Partial/PartialProcessor.php: -------------------------------------------------------------------------------- 1 | process($node, $configs); 18 | } 19 | 20 | public function processConfiguration(ConfigurationInterface $configuration, $breadcrumbPath, array $configs) 21 | { 22 | return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $breadcrumbPath, $configs); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2017 Matthias Noback 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matthiasnoback/symfony-config-test", 3 | "type": "library", 4 | "description": "Library for testing user classes related to the Symfony Config Component", 5 | "keywords": ["symfony", "config", "phpunit", "testing"], 6 | "homepage": "https://github.com/matthiasnoback/SymfonyConfigTest", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Matthias Noback", 11 | "email": "matthiasnoback@gmail.com", 12 | "homepage": "http://php-and-symfony.matthiasnoback.nl" 13 | } 14 | ], 15 | "require": { 16 | "php": "^8.1", 17 | "phpunit/phpunit": "^10.5 || ^11.0 || ^12.0", 18 | "symfony/config": "^5.4 || ^6.4 || ^7.0 || ^8.0" 19 | }, 20 | "autoload": { 21 | "psr-4" : { "Matthias\\SymfonyConfigTest\\" : "" }, 22 | "exclude-from-classmap": ["/Tests/"] 23 | }, 24 | "autoload-dev": { 25 | "psr-4" : { "Matthias\\SymfonyConfigTest\\Tests\\" : "Tests/" } 26 | }, 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "6.x-dev" 30 | } 31 | }, 32 | "minimum-stability": "dev", 33 | "prefer-stable": true 34 | } 35 | -------------------------------------------------------------------------------- /PhpUnit/ProcessedConfigurationEqualsConstraint.php: -------------------------------------------------------------------------------- 1 | validateConfigurationValuesArray($configurationValues); 18 | $this->configurationValues = $configurationValues; 19 | 20 | parent::__construct($configuration, $breadcrumbPath); 21 | } 22 | 23 | public function evaluate($other, $description = '', $returnResult = false): ?bool 24 | { 25 | $processedConfiguration = $this->processConfiguration($this->configurationValues); 26 | 27 | $constraint = new IsEqual($other); 28 | 29 | return $constraint->evaluate($processedConfiguration, '', $returnResult); 30 | } 31 | 32 | public function toString(): string 33 | { 34 | // won't be used, this constraint only wraps IsEqual 35 | return ''; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /PhpUnit/ConfigurationValuesAreValidConstraint.php: -------------------------------------------------------------------------------- 1 | validateConfigurationValuesArray($other); 18 | 19 | $success = true; 20 | 21 | try { 22 | $this->processConfiguration($other); 23 | } catch (InvalidConfigurationException $exception) { 24 | $success = false; 25 | $description = empty($description) ? $exception->getMessage() : $description."\n".$exception->getMessage(); 26 | } 27 | 28 | if ($returnResult) { 29 | return $success; 30 | } 31 | 32 | if (!$success) { 33 | $this->fail($other, $description); 34 | } 35 | 36 | return null; 37 | } 38 | 39 | public function toString(): string 40 | { 41 | return 'is valid for the given configuration'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PhpUnit/AbstractConfigurationConstraint.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 17 | $this->breadcrumbPath = $breadcrumbPath; 18 | } 19 | 20 | protected function processConfiguration(array $configurationValues) 21 | { 22 | $processor = new PartialProcessor(); 23 | 24 | return $processor->processConfiguration($this->configuration, $this->breadcrumbPath, $configurationValues); 25 | } 26 | 27 | protected function validateConfigurationValuesArray($configurationValues) 28 | { 29 | if (!is_array($configurationValues)) { 30 | throw new \InvalidArgumentException('Configuration values should be an array'); 31 | } 32 | 33 | foreach ($configurationValues as $values) { 34 | if (!is_array($values) && null !== $values) { 35 | throw new \InvalidArgumentException('Configuration values should be an array of arrays'); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v4.3.0 4 | 5 | - Added support for Symfony 6, 6 | - Removed support for Symfony <4.4 & >5.0 - <5.3 7 | 8 | ## 4.2.1 9 | 10 | - Support for PHP 8. 11 | - Updated namespace for tests. 12 | 13 | ## 4.2.0 14 | 15 | - Support for PHPUnit9. 16 | 17 | ## 4.1.0 18 | 19 | - Support for Symfony5. 20 | 21 | ## 4.0.1 22 | 23 | - Support for PHPUnit8. 24 | 25 | ## 4.0.0 26 | 27 | - Dropped support for PHPUnit < 7.0 28 | - [BC Break] Add return type hints to all `toString()` methods of the constraint classes for compatibility with PHPUnit 7 29 | - Dropped support for PHP < 7.1 30 | - Allow for completely empty configuration 31 | 32 | ## 3.1.0 33 | 34 | - Support for Symfony 4 35 | 36 | ## 3.0.1 37 | 38 | - Only support Symfony 2.* and 3.* LTS versions 39 | - Require PHP ^7.0 40 | - Drop support for HHVM 41 | 42 | ## 3.0.0 43 | 44 | - Only support PHPUnit 6 45 | - Only support Symfony 2.* and 3.* LTS versions 46 | - Require PHP ^7.0 47 | - Drop support for HHVM 48 | - Deprecated `Matthias\SymfonyConfigTest\PhpUnit\AbstractConfigurationTestCase`, use `Matthias\SymfonyConfigTest\PhpUnit\ConfigurationTestCaseTrait` instead. 49 | 50 | ## 2.0.0 51 | 52 | - Use the breadcrumb path to test one particular part of a prototype node. 53 | - Only support Symfony 2.* LTS versions; only support PHPUnit 4.* and 5.*. 54 | 55 | ## 1.2.0 56 | 57 | - Use a breadcrumb path to test only one particular part of the configuration node tree. 58 | 59 | ## 1.1.0 60 | 61 | - Add a trait for test cases, mark the abstract base class "deprecated" 62 | 63 | ## v0.1.1 64 | 65 | - Fixed issue #1: ``ProcessedConfigurationEqualsConstraint`` had expected and actual value mixed up 66 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | tests: 9 | name: PHPUnit PHP ${{ matrix.php }} ${{ matrix.dependency }} (Symfony ${{ matrix.symfony }}) 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | php: 14 | - '8.1' 15 | - '8.2' 16 | - '8.3' 17 | - '8.4' 18 | dependency: 19 | - '' 20 | symfony: 21 | - '5.4.*' 22 | - '6.4.*' 23 | - '7.3.*' 24 | include: 25 | - php: '8.1' 26 | symfony: '5.4.*' 27 | dependency: 'lowest' 28 | - php: '8.4' 29 | symfony: '7.4.*@dev' 30 | dependency: '' 31 | - php: '8.4' 32 | symfony: '8.0.*@dev' 33 | dependency: '' 34 | exclude: 35 | - php: '8.1' 36 | symfony: '7.3.*' 37 | fail-fast: false 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | 42 | - name: Setup PHP 43 | uses: shivammathur/setup-php@v2 44 | with: 45 | php-version: ${{ matrix.php }} 46 | extensions: pcov 47 | tools: flex 48 | 49 | - name: Get Composer Cache Directory 50 | id: composer-cache 51 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 52 | 53 | - name: Cache dependencies 54 | uses: actions/cache@v4 55 | with: 56 | path: ${{ steps.composer-cache.outputs.dir }} 57 | key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} 58 | restore-keys: ${{ matrix.php }}-composer- 59 | 60 | - name: Update project dependencies 61 | if: matrix.dependency == '' 62 | run: composer update --no-progress --ansi --prefer-stable 63 | env: 64 | SYMFONY_REQUIRE: ${{ matrix.symfony }} 65 | 66 | - name: Update project dependencies lowest 67 | if: matrix.dependency == 'lowest' 68 | run: composer update --no-progress --ansi --prefer-stable --prefer-lowest 69 | env: 70 | SYMFONY_REQUIRE: ${{ matrix.symfony }} 71 | 72 | - name: Validate composer 73 | run: composer validate --strict --no-check-lock 74 | 75 | - name: Run tests 76 | run: vendor/bin/phpunit 77 | -------------------------------------------------------------------------------- /Partial/PartialNode.php: -------------------------------------------------------------------------------- 1 | getValue($node); 55 | foreach ($children as $name => $child) { 56 | if ($name !== $nextNodeName) { 57 | unset($children[$name]); 58 | } 59 | } 60 | self::nodeChildrenProperty()->setValue($node, $children); 61 | 62 | if (!($nextNode instanceof ArrayNode)) { 63 | if (!empty($path)) { 64 | throw new ChildIsNotAnArrayNode($node, $nextNodeName); 65 | } 66 | 67 | return; 68 | } 69 | 70 | self::excludeEverythingNotInPath($nextNode, $path); 71 | } 72 | 73 | /** 74 | * @param ArrayNode $node 75 | * @param string $childNodeName 76 | * 77 | * @return NodeInterface 78 | */ 79 | private static function childNode(ArrayNode $node, $childNodeName) 80 | { 81 | if ($node instanceof PrototypedArrayNode && '*' === $childNodeName) { 82 | return self::nodePrototypeProperty()->getValue($node); 83 | } 84 | 85 | $children = self::nodeChildrenProperty()->getValue($node); 86 | 87 | if (!isset($children[$childNodeName])) { 88 | throw new UndefinedChildNode( 89 | $node, 90 | $childNodeName 91 | ); 92 | } 93 | 94 | return $children[$childNodeName]; 95 | } 96 | 97 | /** 98 | * @return \ReflectionProperty 99 | */ 100 | private static function nodeChildrenProperty() 101 | { 102 | return self::$nodeChildrenProperty ??= new \ReflectionProperty( 103 | ArrayNode::class, 104 | 'children' 105 | ); 106 | } 107 | 108 | /** 109 | * @return \ReflectionProperty 110 | */ 111 | private static function nodePrototypeProperty() 112 | { 113 | return self::$nodePrototypeProperty ??= new \ReflectionProperty( 114 | PrototypedArrayNode::class, 115 | 'prototype' 116 | ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /PhpUnit/ConfigurationValuesAreInvalidConstraint.php: -------------------------------------------------------------------------------- 1 | expectedMessage = $expectedMessage; 28 | $this->useRegExp = $useRegExp; 29 | } 30 | 31 | public function evaluate($other, $description = '', $returnResult = false): ?bool 32 | { 33 | $this->validateConfigurationValuesArray($other); 34 | 35 | try { 36 | $this->processConfiguration($other); 37 | } catch (InvalidConfigurationException $exception) { 38 | return $this->evaluateException($exception, $description, $returnResult); 39 | } 40 | 41 | if ($returnResult) { 42 | return false; 43 | } 44 | 45 | $this->fail($other, $description); 46 | 47 | return null; 48 | } 49 | 50 | public function toString(): string 51 | { 52 | $toString = 'is invalid for the given configuration'; 53 | 54 | if ($this->expectedMessage !== null) { 55 | $toString .= ' (expected exception message: '.$this->expectedMessage.')'; 56 | } 57 | 58 | return $toString; 59 | } 60 | 61 | private function evaluateException(\Exception $exception, $description, $returnResult) 62 | { 63 | if ($this->expectedMessage === null) { 64 | return true; 65 | } 66 | 67 | return $this->createPhpUnitConstraint() 68 | ->evaluate($exception, $description, $returnResult); 69 | } 70 | 71 | private function createPhpUnitConstraint() 72 | { 73 | if ($this->useRegExp) { 74 | // Available since PHPUnit 10.0.15 75 | if (class_exists(ExceptionMessageMatchesRegularExpression::class)) { 76 | return new ExceptionMessageMatchesRegularExpression($this->expectedMessage); 77 | } 78 | 79 | // Available between PHPUnit 10.0.0 and 10.0.14 (inclusive) 80 | if (class_exists(MessageMatchesRegularExpression::class)) { 81 | return new MessageMatchesRegularExpression('exception', $this->expectedMessage); 82 | } 83 | 84 | // Available in PHPUnit 9.6 85 | return new ExceptionMessageRegularExpression($this->expectedMessage); 86 | } 87 | 88 | // Available since PHPUnit 10.0.15 89 | if (class_exists(ExceptionMessageIsOrContains::class)) { 90 | return new ExceptionMessageIsOrContains($this->expectedMessage); 91 | } 92 | 93 | // Available between PHPUnit 10.0.0 and 10.0.14 (inclusive) 94 | if (class_exists(MessageIsOrContains::class)) { 95 | return new MessageIsOrContains('exception', $this->expectedMessage); 96 | } 97 | 98 | // Available in PHPUnit 9.6 99 | return new ExceptionMessage($this->expectedMessage); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /PhpUnit/ConfigurationTestCaseTrait.php: -------------------------------------------------------------------------------- 1 | getConfiguration(), 39 | $expectedMessage, 40 | $useRegExp 41 | ) 42 | ); 43 | } 44 | 45 | /** 46 | * Assert that the given configuration values are invalid. 47 | * 48 | * Optionally provide (part of) the exception message that you expect to receive. 49 | * 50 | * You need to set useRegExp to true if you'd like 51 | * to match the exception message using a regular expression. 52 | * 53 | * @param array $configurationValues 54 | * @param string $breadcrumbPath The path that should be validated, e.g. "doctrine.orm" 55 | * @param string|null $expectedMessage 56 | * @param bool $useRegExp 57 | */ 58 | protected function assertPartialConfigurationIsInvalid( 59 | array $configurationValues, 60 | $breadcrumbPath, 61 | $expectedMessage = null, 62 | $useRegExp = false 63 | ) { 64 | TestCase::assertThat( 65 | $configurationValues, 66 | new ConfigurationValuesAreInvalidConstraint( 67 | $this->getConfiguration(), 68 | $expectedMessage, 69 | $useRegExp, 70 | $breadcrumbPath 71 | ) 72 | ); 73 | } 74 | 75 | /** 76 | * Assert that the given configuration values are valid. 77 | * 78 | * Optionally provide the part of the configuration that you want to test, e.g. "doctrine.orm" 79 | * 80 | * @param array $configurationValues 81 | * @param string|null $breadcrumbPath 82 | */ 83 | protected function assertConfigurationIsValid(array $configurationValues, $breadcrumbPath = null) 84 | { 85 | TestCase::assertThat( 86 | $configurationValues, 87 | new ConfigurationValuesAreValidConstraint( 88 | $this->getConfiguration(), 89 | $breadcrumbPath 90 | ) 91 | ); 92 | } 93 | 94 | /** 95 | * Assert that the given configuration values, when processed, will equal to the given array. 96 | * 97 | * Optionally provide the part of the configuration that you want to test, e.g. "doctrine.orm" 98 | * 99 | * @param array $configurationValues 100 | * @param array $expectedProcessedConfiguration 101 | * @param string|null $breadcrumbPath 102 | */ 103 | protected function assertProcessedConfigurationEquals( 104 | array $configurationValues, 105 | array $expectedProcessedConfiguration, 106 | $breadcrumbPath = null 107 | ) { 108 | TestCase::assertThat( 109 | $expectedProcessedConfiguration, 110 | new ProcessedConfigurationEqualsConstraint( 111 | $this->getConfiguration(), 112 | $configurationValues, 113 | $breadcrumbPath 114 | ) 115 | ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Symfony Config Test 2 | 3 | *By Matthias Noback and contributors* 4 | 5 | [![Build Status](https://github.com/SymfonyTest/SymfonyConfigTest/actions/workflows/ci.yaml/badge.svg)](https://github.com/SymfonyTest/SymfonyConfigTest/actions/workflows/ci.yaml) 6 | 7 | Writing configuration classes using the [Symfony Config 8 | Component](https://symfony.com/doc/current/components/config/definition.html) can be quite hard. To help you verify the 9 | validity of the resulting config node tree, this library provides a PHPUnit test case and some custom assertions. 10 | 11 | ## Installation 12 | 13 | Using Composer: 14 | 15 | composer require --dev matthiasnoback/symfony-config-test 16 | 17 | ## Usage 18 | 19 | Create a test case and use the trait from ``Matthias\SymfonyConfigTest\PhpUnit\ConfigurationTestCaseTrait``. 20 | Then implement ``getConfiguration()``: 21 | 22 | ```php 23 | root('root'); 57 | $rootNode 58 | ->isRequired() 59 | ->children() 60 | ->scalarNode('required_value') 61 | ->isRequired() 62 | ->end() 63 | ->end(); 64 | 65 | return $treeBuilder; 66 | } 67 | } 68 | ``` 69 | 70 | When you provide an empty array as the value for this configuration, you would expect an exception since the 71 | ``required_value`` node is required. You can assert that a given set of configuration values is invalid using the 72 | ``assertConfigurationIsInvalid()`` method: 73 | 74 | ```php 75 | assertConfigurationIsInvalid( 90 | [ 91 | [] // no values at all 92 | ], 93 | 'required_value' // (part of) the expected exception message - optional 94 | ); 95 | } 96 | } 97 | ``` 98 | 99 | ### Test processed configuration values 100 | 101 | You may also want to verify that after processing an array of configuration values the result will be as expected: 102 | 103 | ```php 104 | assertProcessedConfigurationEquals([ 119 | ['required_value' => 'first value'], 120 | ['required_value' => 'last value'] 121 | ], [ 122 | 'required_value'=> 'last value' 123 | ]); 124 | } 125 | } 126 | ``` 127 | 128 | Please note: the first argument of each of the ``assert*`` methods is an *array of arrays*. The extra nesting level 129 | allows you to test the merge process. See also the section [Merging 130 | options](https://symfony.com/doc/current/components/config/definition.html#merging-options) of the Config Component 131 | documentation. 132 | 133 | ### Test a subset of the configuration tree 134 | 135 | Using this library it's possible to test just one branch of your configuration tree. Consider the following node tree 136 | definition, which contains the branches `array_node_1` and `array_node_2`: 137 | 138 | ```php 139 | root('root'); 151 | $rootNode 152 | ->children() 153 | ->arrayNode('array_node_1') 154 | ->isRequired() 155 | ->children() 156 | ->scalarNode('required_value_1') 157 | ->isRequired() 158 | ->end() 159 | ->end() 160 | ->end() 161 | ->arrayNode('array_node_2') 162 | ->isRequired() 163 | ->children() 164 | ->scalarNode('required_value_2') 165 | ->isRequired() 166 | ->end() 167 | ->end() 168 | ->end() 169 | ->end(); 170 | 171 | return $treeBuilder; 172 | } 173 | } 174 | ``` 175 | 176 | If you want to test, for instance, only the `array_node_1` branch from the example below, and ignore the `array_node_2`, 177 | provide `array_node_1` as the argument for the `$breadcrumbPath` parameter of the test helper functions, for example: 178 | 179 | ```php 180 | /** 181 | * @test 182 | */ 183 | public function processed_configuration_for_array_node_1(): void 184 | { 185 | $this->assertProcessedConfigurationEquals( 186 | [ 187 | ['array_node_1' => ['required_value_1' => 'original value']], 188 | ['array_node_1' => ['required_value_1' => 'final value']] 189 | ], 190 | [ 191 | 'array_node_1' => [ 192 | 'required_value_1' => 'final value' 193 | ] 194 | ], 195 | // the path of the nodes you want to focus on in this test: 196 | 'array_node_1' 197 | ); 198 | } 199 | ``` 200 | 201 | This would trigger no validation errors for any value in the `array_node_2` branch. 202 | 203 | Note that the `$breadcrumbPath` can be even more specific, e.g. `"doctrine.orm"` (which would skip configuration 204 | processing for branch `"doctrine.dbal"`, etc.). 205 | 206 | Also note that you can only traverse over array nodes using the `.` in the breadcrumb path. The last part of the breadcrumb path can be any other type of node. 207 | 208 | #### Test a subset of the prototyped configuration tree 209 | 210 | You can traverse through prototype array nodes using `*` as its name in the breadcrumb path. 211 | 212 | ```php 213 | root('root'); 225 | $rootNode 226 | ->children() 227 | ->arrayNode('array_node') 228 | ->useAttributeAsKey('name') 229 | ->prototype('array') 230 | ->children() 231 | ->scalarNode('default_value')->cannotBeEmpty()->defaultValue('foobar')->end() 232 | ->scalarNode('required_value')->isRequired()->end() 233 | ->end() 234 | ->end() 235 | ->end() 236 | ->end(); 237 | 238 | return $treeBuilder; 239 | } 240 | } 241 | ``` 242 | 243 | If you want to test whether `default_value` is set to `foobar` by default, but don't want the test to be affected by 244 | requirements on `required_value` node, you can define its path as `array_node.*.default_value`, for example: 245 | 246 | ```php 247 | /** 248 | * @test 249 | */ 250 | public function processed_configuration_for_array_node_1(): void 251 | { 252 | $this->assertProcessedConfigurationEquals( 253 | [ 254 | ['array_node' => ['prototype_name' => null]], 255 | ], 256 | [ 257 | 'array_node' => [ 258 | 'prototype_name' => [ 259 | 'default_value' => 'foobar' 260 | ] 261 | ] 262 | ], 263 | // the path of the nodes you want to focus on in this test: 264 | 'array_node.*.default_value' 265 | ); 266 | } 267 | ``` 268 | 269 | ## Version Guidance 270 | 271 | | Version | Released | PHPUnit | Status | 272 | |---------|--------------|------------------------|----------| 273 | | 6.x | Feb 7, 2025 | 10.5 and 11.x and 12.x | Latest | 274 | | 5.x | Jun 2, 2023 | 9.6 and 10.x and 11.x | Bugfixes | 275 | | 4.x | Mar 5, 2018 | 7.x and 8.x and 9.x | EOL | 276 | | 3.x | Nov 30, 2017 | 6.x | EOL | 277 | | 2.x | Jun 18, 2016 | 4.x and 5.x | EOL | 278 | | 1.x | Oct 12, 2014 | 3.x | EOL | 279 | --------------------------------------------------------------------------------