Repository: PHP-CS-Fixer/PHP-CS-Fixer (php)

Issue:
PhpUnitDedicateAssertFixer is replacing functions with deprecated/removed method names of PHPUnit v9.1+
### Problem description

Since PHPUnit version 9.1 some of the dedicated asserts methods are renamed to a more readable name.
E.g. assertDirectoryNotExists() is renamed to assertDirectoryDoesNotExists()
(see list on https://github.com/sebastianbergmann/phpunit/blob/9.1.0/ChangeLog-9.1.md)
Since PHPUnit version 10 these deprecated methods are removed.

The PhpUnitDedicateAssertFixer is still referencing to the old names 
https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php.
So when i'm using PHPUnit version 10+ the fixer will break my code.
it will not work anymore.

### Minimal reproducer

Using the rulset: @PHPUnit100Migration:risky with this code:

```php
declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class test extends TestCase
{
    public function testNotDirectory(): void
    {
        self::assertFalse(is_dir('/doesnotexists'));
        self::assertFalse(file_exists('/doesnotexists.txt'));
    }
}
```

Result:
```
C:\xxxxx\test.php (php_unit_dedicate_assert)
      ---------- begin diff ----------
--- C:\xxxx\test.php
+++ C:\xxxxx\test.php
@@ -8,7 +8,7 @@
 {
     public function testNotDirectory(): void
     {
-        self::assertFalse(is_dir('/doesnotexists'));
-        self::assertFalse(file_exists('/doesnotexists.txt'));
+        self::assertDirectoryNotExists('/doesnotexists');
+        self::assertFileNotExists('/doesnotexists.txt');
     }
 }

      ----------- end diff -----------
```

Must be when using PHPUnit 9.1+:
```
C:\xxxxx\test.php (php_unit_dedicate_assert)
      ---------- begin diff ----------
--- C:\xxxx\test.php
+++ C:\xxxxx\test.php
@@ -8,7 +8,7 @@
 {
     public function testNotDirectory(): void
     {
-        self::assertFalse(is_dir('/doesnotexists'));
-        self::assertFalse(file_exists('/doesnotexists.txt'));
+        self::assertDirectoryDoesNotExist('/doesnotexists');
+        self::assertFileDoesNotExist('/doesnotexists.txt');
     }
 }

      ----------- end diff -----------
```







### Fixer version

3.54.0

### PHP version

8.3.6

### How do you run Fixer?

Composer package

### Contribution Checks

- [X] I have verified if this problem was already reported
- [X] I am familiar with ["Feature or bug?"](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/feature-or-bug.rst)

Pull request description:
feat: support PHPUnit v9.1 naming for some asserts
Fixes #7968

~~I assumed `$fixMap` is static for performance so I tried my best to keep it like that.~~
Could not keep fixmap static, I don't know if it has impact on performace, not sure how to use benchmark

Patch:
diff --git a/doc/ruleSets/PHPUnit100MigrationRisky.rst b/doc/ruleSets/PHPUnit100MigrationRisky.rst
index ea7ef946b..ae2134d87 100644
--- a/doc/ruleSets/PHPUnit100MigrationRisky.rst
+++ b/doc/ruleSets/PHPUnit100MigrationRisky.rst
@@ -15,7 +15,7 @@ Using this rule set may lead to changes in your code's logic and behaviour. Use
 Rules
 -----
 
-- `@PHPUnit84Migration:risky <./PHPUnit84MigrationRisky.rst>`_
+- `@PHPUnit91Migration:risky <./PHPUnit91MigrationRisky.rst>`_
 - `php_unit_data_provider_static <./../rules/php_unit/php_unit_data_provider_static.rst>`_ with config:
 
   ``['force' => true]``
diff --git a/doc/ruleSets/PHPUnit91MigrationRisky.rst b/doc/ruleSets/PHPUnit91MigrationRisky.rst
new file mode 100644
index 000000000..4a599ad0e
--- /dev/null
+++ b/doc/ruleSets/PHPUnit91MigrationRisky.rst
@@ -0,0 +1,19 @@
+======================================
+Rule set ``@PHPUnit91Migration:risky``
+======================================
+
+Rules to improve tests code for PHPUnit 9.1 compatibility.
+
+Warning
+-------
+
+This set contains rules that are risky
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Using this rule set may lead to changes in your code's logic and behaviour. Use it with caution and review changes before incorporating them into your code base.
+
+Rules
+-----
+
+- `@PHPUnit84Migration:risky <./PHPUnit84MigrationRisky.rst>`_
+- `php_unit_assert_new_names <./../rules/php_unit/php_unit_assert_new_names.rst>`_
diff --git a/doc/ruleSets/index.rst b/doc/ruleSets/index.rst
index afe3dc738..b4473e3e5 100644
--- a/doc/ruleSets/index.rst
+++ b/doc/ruleSets/index.rst
@@ -39,6 +39,7 @@ List of Available Rule sets
 - `@PHPUnit60Migration:risky <./PHPUnit60MigrationRisky.rst>`_
 - `@PHPUnit75Migration:risky <./PHPUnit75MigrationRisky.rst>`_
 - `@PHPUnit84Migration:risky <./PHPUnit84MigrationRisky.rst>`_
+- `@PHPUnit91Migration:risky <./PHPUnit91MigrationRisky.rst>`_
 - `@PHPUnit100Migration:risky <./PHPUnit100MigrationRisky.rst>`_
 - `@PSR1 <./PSR1.rst>`_
 - `@PSR2 <./PSR2.rst>`_
diff --git a/doc/rules/index.rst b/doc/rules/index.rst
index 231e0c478..c7343131f 100644
--- a/doc/rules/index.rst
+++ b/doc/rules/index.rst
@@ -640,6 +640,9 @@ PHP Tag
 PHPUnit
 -------
 
+- `php_unit_assert_new_names <./php_unit/php_unit_assert_new_names.rst>`_ *(risky)*
+
+  Rename deprecated PHPUnit assertions like ``assertFileNotExists`` to new methods like ``assertFileDoesNotExist``.
 - `php_unit_attributes <./php_unit/php_unit_attributes.rst>`_
 
   PHPUnit attributes must be used over their respective PHPDoc-based annotations.
diff --git a/doc/rules/php_unit/php_unit_assert_new_names.rst b/doc/rules/php_unit/php_unit_assert_new_names.rst
new file mode 100644
index 000000000..aa7085713
--- /dev/null
+++ b/doc/rules/php_unit/php_unit_assert_new_names.rst
@@ -0,0 +1,52 @@
+==================================
+Rule ``php_unit_assert_new_names``
+==================================
+
+Rename deprecated PHPUnit assertions like ``assertFileNotExists`` to new methods
+like ``assertFileDoesNotExist``.
+
+Warning
+-------
+
+Using this rule is risky
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Fixer could be risky if one is overriding PHPUnit's native methods.
+
+Examples
+--------
+
+Example #1
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+    final class MyTest extends \PHPUnit_Framework_TestCase
+    {
+        public function testSomeTest()
+        {
+   -        $this->assertFileNotExists("test.php");
+   -        $this->assertNotIsWritable("path.php");
+   +        $this->assertFileDoesNotExist("test.php");
+   +        $this->assertIsNotWritable("path.php");
+        }
+    }
+
+Rule sets
+---------
+
+The rule is part of the following rule sets:
+
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_
+- `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_
+
+References
+----------
+
+- Fixer class: `PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitAssertNewNamesFixer <./../../../src/Fixer/PhpUnit/PhpUnitAssertNewNamesFixer.php>`_
+- Test class: `PhpCsFixer\\Tests\\Fixer\\PhpUnit\\PhpUnitAssertNewNamesFixerTest <./../../../tests/Fixer/PhpUnit/PhpUnitAssertNewNamesFixerTest.php>`_
+
+The test class defines officially supported behaviour. Each test case is a part of our backward compatibility promise.
diff --git a/doc/rules/php_unit/php_unit_dedicate_assert.rst b/doc/rules/php_unit/php_unit_dedicate_assert.rst
index c0b254c81..ee605bca2 100644
--- a/doc/rules/php_unit/php_unit_dedicate_assert.rst
+++ b/doc/rules/php_unit/php_unit_dedicate_assert.rst
@@ -133,6 +133,10 @@ The rule is part of the following rule sets:
 
   ``['target' => '5.6']``
 
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_ with config:
+
+  ``['target' => '5.6']``
+
 - `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_ with config:
 
   ``['target' => '5.6']``
diff --git a/doc/rules/php_unit/php_unit_dedicate_assert_internal_type.rst b/doc/rules/php_unit/php_unit_dedicate_assert_internal_type.rst
index 92ce289be..23c37b304 100644
--- a/doc/rules/php_unit/php_unit_dedicate_assert_internal_type.rst
+++ b/doc/rules/php_unit/php_unit_dedicate_assert_internal_type.rst
@@ -84,6 +84,10 @@ The rule is part of the following rule sets:
 
   ``['target' => '7.5']``
 
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_ with config:
+
+  ``['target' => '7.5']``
+
 - `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_ with config:
 
   ``['target' => '7.5']``
diff --git a/doc/rules/php_unit/php_unit_expectation.rst b/doc/rules/php_unit/php_unit_expectation.rst
index dc339529f..cb8a139d4 100644
--- a/doc/rules/php_unit/php_unit_expectation.rst
+++ b/doc/rules/php_unit/php_unit_expectation.rst
@@ -185,6 +185,10 @@ The rule is part of the following rule sets:
 
   ``['target' => '8.4']``
 
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_ with config:
+
+  ``['target' => '8.4']``
+
 - `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_ with config:
 
   ``['target' => '8.4']``
diff --git a/doc/rules/php_unit/php_unit_mock.rst b/doc/rules/php_unit/php_unit_mock.rst
index 9cc1e13d8..4263e5bf9 100644
--- a/doc/rules/php_unit/php_unit_mock.rst
+++ b/doc/rules/php_unit/php_unit_mock.rst
@@ -106,6 +106,10 @@ The rule is part of the following rule sets:
 
   ``['target' => '5.5']``
 
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_ with config:
+
+  ``['target' => '5.5']``
+
 - `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_ with config:
 
   ``['target' => '5.5']``
diff --git a/doc/rules/php_unit/php_unit_namespaced.rst b/doc/rules/php_unit/php_unit_namespaced.rst
index 98b47d582..9f28dedc8 100644
--- a/doc/rules/php_unit/php_unit_namespaced.rst
+++ b/doc/rules/php_unit/php_unit_namespaced.rst
@@ -126,6 +126,10 @@ The rule is part of the following rule sets:
 
   ``['target' => '6.0']``
 
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_ with config:
+
+  ``['target' => '6.0']``
+
 - `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_ with config:
 
   ``['target' => '6.0']``
diff --git a/doc/rules/php_unit/php_unit_no_expectation_annotation.rst b/doc/rules/php_unit/php_unit_no_expectation_annotation.rst
index cf73e00c4..728063e7a 100644
--- a/doc/rules/php_unit/php_unit_no_expectation_annotation.rst
+++ b/doc/rules/php_unit/php_unit_no_expectation_annotation.rst
@@ -153,6 +153,10 @@ The rule is part of the following rule sets:
 
   ``['target' => '4.3']``
 
+- `@PHPUnit91Migration:risky <./../../ruleSets/PHPUnit91MigrationRisky.rst>`_ with config:
+
+  ``['target' => '4.3']``
+
 - `@PHPUnit100Migration:risky <./../../ruleSets/PHPUnit100MigrationRisky.rst>`_ with config:
 
   ``['target' => '4.3']``
diff --git a/src/Fixer/AbstractPhpUnitFixer.php b/src/Fixer/AbstractPhpUnitFixer.php
index 0b4c4b2f5..b6a8660c7 100644
--- a/src/Fixer/AbstractPhpUnitFixer.php
+++ b/src/Fixer/AbstractPhpUnitFixer.php
@@ -19,6 +19,7 @@
 use PhpCsFixer\DocBlock\Line;
 use PhpCsFixer\Indicator\PhpUnitTestCaseIndicator;
 use PhpCsFixer\Tokenizer\Analyzer\AttributeAnalyzer;
+use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
 use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
 use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
 use PhpCsFixer\Tokenizer\CT;
@@ -98,6 +99,52 @@ final protected function isPHPDoc(Tokens $tokens, int $index): bool
         return $tokens[$index]->isGivenKind(T_DOC_COMMENT);
     }
 
+    /**
+     * @return iterable<array{
+     *     index: int,
+     *     loweredName: string,
+     *     openBraceIndex: int,
+     *     closeBraceIndex: int,
+     * }>
+     */
+    protected function getPreviousAssertCall(Tokens $tokens, int $startIndex, int $endIndex): iterable
+    {
+        $functionsAnalyzer = new FunctionsAnalyzer();
+
+        for ($index = $endIndex; $index > $startIndex; --$index) {
+            $index = $tokens->getPrevTokenOfKind($index, [[T_STRING]]);
+
+            if (null === $index) {
+                return;
+            }
+
+            // test if "assert" something call
+            $loweredContent = strtolower($tokens[$index]->getContent());
+
+            if (!str_starts_with($loweredContent, 'assert')) {
+                continue;
+            }
+
+            // test candidate for simple calls like: ([\]+'some fixable call'(...))
+            $openBraceIndex = $tokens->getNextMeaningfulToken($index);
+
+            if (!$tokens[$openBraceIndex]->equals('(')) {
+                continue;
+            }
+
+            if (!$functionsAnalyzer->isTheSameClassCall($tokens, $index)) {
+                continue;
+            }
+
+            yield [
+                'index' => $index,
+                'loweredName' => $loweredContent,
+                'openBraceIndex' => $openBraceIndex,
+                'closeBraceIndex' => $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex),
+            ];
+        }
+    }
+
     private function createDocBlock(Tokens $tokens, int $docBlockIndex, string $annotation): void
     {
         $lineEnd = $this->whitespacesConfig->getLineEnding();
diff --git a/src/Fixer/PhpUnit/PhpUnitAssertNewNamesFixer.php b/src/Fixer/PhpUnit/PhpUnitAssertNewNamesFixer.php
new file mode 100644
index 000000000..71f02b5df
--- /dev/null
+++ b/src/Fixer/PhpUnit/PhpUnitAssertNewNamesFixer.php
@@ -0,0 +1,107 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Fixer\PhpUnit;
+
+use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author Krzysztof Ciszewski <krzysztof@ciszew.ski>
+ */
+final class PhpUnitAssertNewNamesFixer extends AbstractPhpUnitFixer
+{
+    public function isRisky(): bool
+    {
+        return true;
+    }
+
+    public function getDefinition(): FixerDefinitionInterface
+    {
+        return new FixerDefinition(
+            'Rename deprecated PHPUnit assertions like `assertFileNotExists` to new methods like `assertFileDoesNotExist`.',
+            [
+                new CodeSample(
+                    '<?php
+final class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testSomeTest()
+    {
+        $this->assertFileNotExists("test.php");
+        $this->assertNotIsWritable("path.php");
+    }
+}
+'
+                ),
+            ],
+            null,
+            'Fixer could be risky if one is overriding PHPUnit\'s native methods.'
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Must run after PhpUnitDedicateAssertFixer.
+     */
+    public function getPriority(): int
+    {
+        return -10;
+    }
+
+    protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
+    {
+        foreach ($this->getPreviousAssertCall($tokens, $startIndex, $endIndex) as $assertCall) {
+            $this->fixAssertNewNames($tokens, $assertCall);
+        }
+    }
+
+    /**
+     * @param array{
+     *     index: int,
+     *     loweredName: string,
+     *     openBraceIndex: int,
+     *     closeBraceIndex: int,
+     * } $assertCall
+     */
+    private function fixAssertNewNames(Tokens $tokens, array $assertCall): void
+    {
+        $replacements = [
+            'assertnotisreadable' => 'assertIsNotReadable',
+            'assertnotiswritable' => 'assertIsNotWritable',
+            'assertdirectorynotexists' => 'assertDirectoryDoesNotExist',
+            'assertfilenotexists' => 'assertFileDoesNotExist',
+            'assertdirectorynotisreadable' => 'assertDirectoryIsNotReadable',
+            'assertdirectorynotiswritable' => 'assertDirectoryIsNotWriteable',
+            'assertfilenotisreadable' => 'assertFileIsNotReadable',
+            'assertfilenotiswritable' => 'assertFileIsNotWriteable',
+            'assertregexp' => 'assertMatchesRegularExpression',
+            'assertnotregexp' => 'assertDoesNotMatchRegularExpression',
+        ];
+        $replacement = $replacements[$assertCall['loweredName']] ?? null;
+
+        if (null === $replacement) {
+            return;
+        }
+
+        $tokens[$assertCall['index']] = new Token([
+            T_STRING,
+            $replacement,
+        ]);
+    }
+}
diff --git a/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php b/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php
index 6f4437255..07688253e 100644
--- a/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php
+++ b/src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php
@@ -24,7 +24,6 @@
 use PhpCsFixer\FixerDefinition\FixerDefinition;
 use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
 use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
-use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
 use PhpCsFixer\Tokenizer\CT;
 use PhpCsFixer\Tokenizer\Token;
 use PhpCsFixer\Tokenizer\Tokens;
@@ -171,7 +170,7 @@ public function testSomeTest()
     /**
      * {@inheritdoc}
      *
-     * Must run before NoUnusedImportsFixer, PhpUnitDedicateAssertInternalTypeFixer.
+     * Must run before NoUnusedImportsFixer, PhpUnitAssertNewNamesFixer, PhpUnitDedicateAssertInternalTypeFixer.
      * Must run after ModernizeStrposFixer, NoAliasFunctionsFixer, PhpUnitConstructFixer.
      */
     public function getPriority(): int
@@ -241,21 +240,18 @@ protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $en
 
         foreach ($this->getPreviousAssertCall($tokens, $startIndex, $endIndex) as $assertCall) {
             // test and fix for assertTrue/False to dedicated asserts
-            if ('asserttrue' === $assertCall['loweredName'] || 'assertfalse' === $assertCall['loweredName']) {
+            if (\in_array($assertCall['loweredName'], ['asserttrue', 'assertfalse'], true)) {
                 $this->fixAssertTrueFalse($tokens, $argumentsAnalyzer, $assertCall);
 
                 continue;
             }
 
-            if (
-                'assertsame' === $assertCall['loweredName']
-                || 'assertnotsame' === $assertCall['loweredName']
-                || 'assertequals' === $assertCall['loweredName']
-                || 'assertnotequals' === $assertCall['loweredName']
-            ) {
+            if (\in_array(
+                $assertCall['loweredName'],
+                ['assertsame', 'assertnotsame', 'assertequals', 'assertnotequals'],
+                true
+            )) {
                 $this->fixAssertSameEquals($tokens, $assertCall);
-
-                continue;
             }
         }
     }
@@ -495,7 +491,7 @@ private function fixAssertSameEquals(Tokens $tokens, array $assertCall): void
 
         $lowerContent = strtolower($tokens[$countCallIndex]->getContent());
 
-        if ('count' !== $lowerContent && 'sizeof' !== $lowerContent) {
+        if (!\in_array($lowerContent, ['count', 'sizeof'], true)) {
             return; // not a call to "count" or "sizeOf"
         }
 
@@ -527,52 +523,6 @@ private function fixAssertSameEquals(Tokens $tokens, array $assertCall): void
         ]);
     }
 
-    /**
-     * @return iterable<array{
-     *     index: int,
-     *     loweredName: string,
-     *     openBraceIndex: int,
-     *     closeBraceIndex: int,
-     * }>
-     */
-    private function getPreviousAssertCall(Tokens $tokens, int $startIndex, int $endIndex): iterable
-    {
-        $functionsAnalyzer = new FunctionsAnalyzer();
-
-        for ($index = $endIndex; $index > $startIndex; --$index) {
-            $index = $tokens->getPrevTokenOfKind($index, [[T_STRING]]);
-
-            if (null === $index) {
-                return;
-            }
-
-            // test if "assert" something call
-            $loweredContent = strtolower($tokens[$index]->getContent());
-
-            if (!str_starts_with($loweredContent, 'assert')) {
-                continue;
-            }
-
-            // test candidate for simple calls like: ([\]+'some fixable call'(...))
-            $openBraceIndex = $tokens->getNextMeaningfulToken($index);
-
-            if (!$tokens[$openBraceIndex]->equals('(')) {
-                continue;
-            }
-
-            if (!$functionsAnalyzer->isTheSameClassCall($tokens, $index)) {
-                continue;
-            }
-
-            yield [
-                'index' => $index,
-                'loweredName' => $loweredContent,
-                'openBraceIndex' => $openBraceIndex,
-                'closeBraceIndex' => $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex),
-            ];
-        }
-    }
-
     private function removeFunctionCall(Tokens $tokens, ?int $callNSIndex, int $callIndex, int $openIndex, int $closeIndex): void
     {
         $tokens->clearTokenAndMergeSurroundingWhitespace($callIndex);
diff --git a/src/Fixer/PhpUnit/PhpUnitTargetVersion.php b/src/Fixer/PhpUnit/PhpUnitTargetVersion.php
index 0599eb4d8..8ecc1c2db 100644
--- a/src/Fixer/PhpUnit/PhpUnitTargetVersion.php
+++ b/src/Fixer/PhpUnit/PhpUnitTargetVersion.php
@@ -37,6 +37,7 @@ final class PhpUnitTargetVersion
     public const VERSION_6_0 = '6.0';
     public const VERSION_7_5 = '7.5';
     public const VERSION_8_4 = '8.4';
+    public const VERSION_9_1 = '9.1';
     public const VERSION_NEWEST = 'newest';
 
     private function __construct() {}
diff --git a/src/RuleSet/Sets/PHPUnit100MigrationRiskySet.php b/src/RuleSet/Sets/PHPUnit100MigrationRiskySet.php
index 433fa7acb..f2092648f 100644
--- a/src/RuleSet/Sets/PHPUnit100MigrationRiskySet.php
+++ b/src/RuleSet/Sets/PHPUnit100MigrationRiskySet.php
@@ -24,7 +24,7 @@ final class PHPUnit100MigrationRiskySet extends AbstractMigrationSetDescription
     public function getRules(): array
     {
         return [
-            '@PHPUnit84Migration:risky' => true,
+            '@PHPUnit91Migration:risky' => true,
             'php_unit_data_provider_static' => ['force' => true],
         ];
     }
diff --git a/src/RuleSet/Sets/PHPUnit91MigrationRiskySet.php b/src/RuleSet/Sets/PHPUnit91MigrationRiskySet.php
new file mode 100644
index 000000000..5f0736d94
--- /dev/null
+++ b/src/RuleSet/Sets/PHPUnit91MigrationRiskySet.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\RuleSet\Sets;
+
+use PhpCsFixer\RuleSet\AbstractMigrationSetDescription;
+
+/**
+ * @internal
+ */
+final class PHPUnit91MigrationRiskySet extends AbstractMigrationSetDescription
+{
+    public function getRules(): array
+    {
+        return [
+            '@PHPUnit84Migration:risky' => true,
+            'php_unit_assert_new_names' => true,
+        ];
+    }
+}


Test patch:
diff --git a/tests/AutoReview/FixerFactoryTest.php b/tests/AutoReview/FixerFactoryTest.php
index 2372215d8..f7bfbb252 100644
--- a/tests/AutoReview/FixerFactoryTest.php
+++ b/tests/AutoReview/FixerFactoryTest.php
@@ -746,6 +746,7 @@ private static function getFixersPriorityGraph(): array
             ],
             'php_unit_dedicate_assert' => [
                 'no_unused_imports',
+                'php_unit_assert_new_names',
                 'php_unit_dedicate_assert_internal_type',
             ],
             'php_unit_fqcn_annotation' => [
diff --git a/tests/Fixer/PhpUnit/PhpUnitAssertNewNamesFixerTest.php b/tests/Fixer/PhpUnit/PhpUnitAssertNewNamesFixerTest.php
new file mode 100644
index 000000000..9f41490f0
--- /dev/null
+++ b/tests/Fixer/PhpUnit/PhpUnitAssertNewNamesFixerTest.php
@@ -0,0 +1,77 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Tests\Fixer\PhpUnit;
+
+use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
+
+/**
+ * @internal
+ *
+ * @covers \PhpCsFixer\Fixer\PhpUnit\PhpUnitAssertNewNamesFixer
+ *
+ * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\PhpUnit\PhpUnitAssertNewNamesFixer>
+ */
+final class PhpUnitAssertNewNamesFixerTest extends AbstractFixerTestCase
+{
+    /**
+     * @dataProvider provideFixCases
+     */
+    public function testFix(string $expected, ?string $input = null): void
+    {
+        $this->doTest($expected, $input);
+    }
+
+    /**
+     * @return iterable<array{0: string, 1: string}>
+     */
+    public static function provideFixCases(): iterable
+    {
+        yield [
+            self::generateTest(
+                '
+                    $this->assertFileDoesNotExist($a);
+                    $this->assertIsNotReadable($a);
+                    $this->assertIsNotWritable($a);
+                    $this->assertDirectoryDoesNotExist($a);
+                    $this->assertDirectoryIsNotReadable($a);
+                    $this->assertDirectoryIsNotWriteable($a);
+                    $this->assertFileIsNotReadable($a);
+                    $this->assertFileIsNotWriteable($a);
+                    $this->assertMatchesRegularExpression($a);
+                    $this->assertDoesNotMatchRegularExpression($a);
+                '
+            ),
+            self::generateTest(
+                '
+                    $this->assertFileNotExists($a);
+                    $this->assertNotIsReadable($a);
+                    $this->assertNotIsWritable($a);
+                    $this->assertDirectoryNotExists($a);
+                    $this->assertDirectoryNotIsReadable($a);
+                    $this->assertDirectoryNotIsWritable($a);
+                    $this->assertFileNotIsReadable($a);
+                    $this->assertFileNotIsWritable($a);
+                    $this->assertRegExp($a);
+                    $this->assertNotRegExp($a);
+                '
+            ),
+        ];
+    }
+
+    private static function generateTest(string $content): string
+    {
+        return "<?php final class FooTest extends \\PHPUnit_Framework_TestCase {\n    public function testSomething() {\n        ".$content."\n    }\n}\n";
+    }
+}
diff --git a/tests/Fixtures/Integration/priority/php_unit_dedicate_assert,php_unit_assert_new_names.test b/tests/Fixtures/Integration/priority/php_unit_dedicate_assert,php_unit_assert_new_names.test
new file mode 100644
index 000000000..1a4c180ab
--- /dev/null
+++ b/tests/Fixtures/Integration/priority/php_unit_dedicate_assert,php_unit_assert_new_names.test
@@ -0,0 +1,21 @@
+--TEST--
+Integration of fixers: php_unit_dedicate_assert,php_unit_assert_new_names.
+--RULESET--
+{"php_unit_dedicate_assert": true, "php_unit_assert_new_names": true}
+--EXPECT--
+<?php
+class FooTest extends \PHPUnit\Framework\TestCase {
+    public function testMe()
+    {
+        $this->assertDirectoryDoesNotExist($a);
+    }
+}
+
+--INPUT--
+<?php
+class FooTest extends \PHPUnit\Framework\TestCase {
+    public function testMe()
+    {
+        $this->assertFalse(is_dir($a));
+    }
+}
diff --git a/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test b/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test
new file mode 100644
index 000000000..0a1758bfe
--- /dev/null
+++ b/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test
@@ -0,0 +1,4 @@
+--TEST--
+Integration of @PHPUnit91Migration:risky.
+--RULESET--
+{"@PHPUnit91Migration:risky": true}
diff --git a/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test-in.php b/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test-in.php
new file mode 100644
index 000000000..872ff4088
--- /dev/null
+++ b/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test-in.php
@@ -0,0 +1,19 @@
+<?php
+
+use PHPUnit_Framework_Assert;
+use PHPUnit_Framework_BaseTestListener;
+use PHPUnit_Framework_TestListener;
+use PHPUnit_Aaa;
+use PHPUnit_Aaa_Bbb;
+use PHPUnit_Aaa_Bbb_Ccc;
+use PHPUnit_Aaa_Bbb_Ccc_Ddd;
+use PHPUnit_Aaa_Bbb_Ccc_Ddd_Eee;
+
+class FooTest extends \PHPUnit_Framework_TestCase {
+    public function test_dedicate_assert($foo) {
+        $this->assertFalse(is_dir($foo));
+        $this->assertFalse(is_writable($foo));
+        $this->assertFalse(file_exists($foo));
+        $this->assertFalse(is_readable($foo));
+    }
+}
diff --git a/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test-out.php b/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test-out.php
new file mode 100644
index 000000000..c45240dd1
--- /dev/null
+++ b/tests/Fixtures/Integration/set/@PHPUnit91Migration-risky.test-out.php
@@ -0,0 +1,19 @@
+<?php
+
+use PHPUnit\Framework\Assert;
+use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\TestListener;
+use PHPUnit\Aaa;
+use PHPUnit\Aaa\Bbb;
+use PHPUnit\Aaa\Bbb\Ccc;
+use PHPUnit\Aaa\Bbb\Ccc\Ddd;
+use PHPUnit\Aaa\Bbb\Ccc\Ddd\Eee;
+
+class FooTest extends \PHPUnit\Framework\TestCase {
+    public function test_dedicate_assert($foo) {
+        $this->assertDirectoryDoesNotExist($foo);
+        $this->assertIsNotWritable($foo);
+        $this->assertFileDoesNotExist($foo);
+        $this->assertIsNotReadable($foo);
+    }
+}
diff --git a/tests/RuleSet/RuleSetsTest.php b/tests/RuleSet/RuleSetsTest.php
index aa75f7c29..4c5bab239 100644
--- a/tests/RuleSet/RuleSetsTest.php
+++ b/tests/RuleSet/RuleSetsTest.php
@@ -83,6 +83,7 @@ public function testHasIntegrationTest(string $setDefinitionName): void
             '@PHPUnit55Migration:risky',
             '@PHPUnit75Migration:risky',
             '@PHPUnit84Migration:risky',
+            '@PHPUnit91Migration:risky',
             '@PHPUnit100Migration:risky',
             '@PSR1',
         ];
diff --git a/tests/RuleSet/Sets/PHPUnit91MigrationRiskySetTest.php b/tests/RuleSet/Sets/PHPUnit91MigrationRiskySetTest.php
new file mode 100644
index 000000000..ce2b74e64
--- /dev/null
+++ b/tests/RuleSet/Sets/PHPUnit91MigrationRiskySetTest.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Tests\RuleSet\Sets;
+
+/**
+ * @internal
+ *
+ * @covers \PhpCsFixer\RuleSet\Sets\PHPUnit91MigrationRiskySet
+ */
+final class PHPUnit91MigrationRiskySetTest extends AbstractSetTestCase {}