Browse Source

feature #3476 Add PhpUnitTestCaseStaticMethodCallsFixer (Slamdunk, keradus)

This PR was squashed before being merged into the 2.12-dev branch (closes #3476).

Discussion
----------

Add PhpUnitTestCaseStaticMethodCallsFixer

```diff
 class MyTest extends \PHPUnit_Framework_TestCase
 {
     public function testMe()
     {
-        $this->assertSame(1, 2);
+        static::assertSame(1, 2);
     }
 }
```

- [x] Convert `PHPUnit\Framework\Assert` static methods
- [x] Convert `PHPUnit\Framework\TestCase` static methods
- [x] Adapt `PhpUnitStrictFixer` to check for `self::` and `static::` calls too: https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/3477
- [x] Adapt `PhpUnitDedicateAssertFixer` to check for `self::` and `static::` calls too: https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/3478
- [x] Adapt `PhpUnitConstructFixer` to check for `self::` and `static::` calls too: https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/3479
- [x] Add to `.php_cs.dist` with `$this->` syntax: only after review

Commits
-------

3cca45ac Add PhpUnitTestCaseStaticMethodCallsFixer
Dariusz Ruminski 6 years ago
parent
commit
efd1b8ad53

+ 1 - 0
.php_cs.dist

@@ -62,6 +62,7 @@ $config = PhpCsFixer\Config::create()
         'php_unit_set_up_tear_down_visibility' => true,
         'php_unit_strict' => true,
         'php_unit_test_annotation' => true,
+        'php_unit_test_case_static_method_calls' => ['call_type' => 'this'],
         'php_unit_test_class_requires_covers' => true,
         'phpdoc_add_missing_param_annotation' => true,
         'phpdoc_order' => true,

+ 14 - 0
README.rst

@@ -1276,6 +1276,20 @@ Choose from the list of available rules:
   - ``style`` (``'annotation'``, ``'prefix'``): whether to use the @test annotation or
     not; defaults to ``'prefix'``
 
+* **php_unit_test_case_static_method_calls**
+
+  Calls to ``PHPUnit\Framework\TestCase`` static methods must all be of the
+  same type, either ``$this->``, ``self::`` or ``static::``.
+
+  *Risky rule: risky when PHPUnit methods are overridden or not accessible, or when project has PHPUnit incompatibilities.*
+
+  Configuration options:
+
+  - ``call_type`` (``'self'``, ``'static'``, ``'this'``): the call type to use for referring
+    to PHPUnit methods; defaults to ``'static'``
+  - ``methods`` (``array``): dictionary of ``method`` => ``call_type`` values that
+    differ from the default strategy; defaults to ``[]``
+
 * **php_unit_test_class_requires_covers**
 
   Adds a default ``@coversNothing`` annotation to PHPUnit test classes that

+ 428 - 0
src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php

@@ -0,0 +1,428 @@
+<?php
+
+/*
+ * 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\AbstractFixer;
+use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
+use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
+use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\Indicator\PhpUnitTestCaseIndicator;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+use PhpCsFixer\Tokenizer\TokensAnalyzer;
+use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
+
+/**
+ * @author Filippo Tessarotto <zoeslam@gmail.com>
+ */
+final class PhpUnitTestCaseStaticMethodCallsFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
+{
+    /**
+     * @internal
+     */
+    const CALL_TYPE_THIS = 'this';
+
+    /**
+     * @internal
+     */
+    const CALL_TYPE_SELF = 'self';
+
+    /**
+     * @internal
+     */
+    const CALL_TYPE_STATIC = 'static';
+
+    private $allowedValues = [
+        self::CALL_TYPE_THIS => true,
+        self::CALL_TYPE_SELF => true,
+        self::CALL_TYPE_STATIC => true,
+    ];
+
+    private $staticMethods = [
+        // Assert methods
+        'anything' => true,
+        'arrayHasKey' => true,
+        'assertArrayHasKey' => true,
+        'assertArrayNotHasKey' => true,
+        'assertArraySubset' => true,
+        'assertAttributeContains' => true,
+        'assertAttributeContainsOnly' => true,
+        'assertAttributeCount' => true,
+        'assertAttributeEmpty' => true,
+        'assertAttributeEquals' => true,
+        'assertAttributeGreaterThan' => true,
+        'assertAttributeGreaterThanOrEqual' => true,
+        'assertAttributeInstanceOf' => true,
+        'assertAttributeInternalType' => true,
+        'assertAttributeLessThan' => true,
+        'assertAttributeLessThanOrEqual' => true,
+        'assertAttributeNotContains' => true,
+        'assertAttributeNotContainsOnly' => true,
+        'assertAttributeNotCount' => true,
+        'assertAttributeNotEmpty' => true,
+        'assertAttributeNotEquals' => true,
+        'assertAttributeNotInstanceOf' => true,
+        'assertAttributeNotInternalType' => true,
+        'assertAttributeNotSame' => true,
+        'assertAttributeSame' => true,
+        'assertClassHasAttribute' => true,
+        'assertClassHasStaticAttribute' => true,
+        'assertClassNotHasAttribute' => true,
+        'assertClassNotHasStaticAttribute' => true,
+        'assertContains' => true,
+        'assertContainsOnly' => true,
+        'assertContainsOnlyInstancesOf' => true,
+        'assertCount' => true,
+        'assertDirectoryExists' => true,
+        'assertDirectoryIsReadable' => true,
+        'assertDirectoryIsWritable' => true,
+        'assertDirectoryNotExists' => true,
+        'assertDirectoryNotIsReadable' => true,
+        'assertDirectoryNotIsWritable' => true,
+        'assertEmpty' => true,
+        'assertEqualXMLStructure' => true,
+        'assertEquals' => true,
+        'assertFalse' => true,
+        'assertFileEquals' => true,
+        'assertFileExists' => true,
+        'assertFileIsReadable' => true,
+        'assertFileIsWritable' => true,
+        'assertFileNotEquals' => true,
+        'assertFileNotExists' => true,
+        'assertFileNotIsReadable' => true,
+        'assertFileNotIsWritable' => true,
+        'assertFinite' => true,
+        'assertGreaterThan' => true,
+        'assertGreaterThanOrEqual' => true,
+        'assertInfinite' => true,
+        'assertInstanceOf' => true,
+        'assertInternalType' => true,
+        'assertIsReadable' => true,
+        'assertIsWritable' => true,
+        'assertJson' => true,
+        'assertJsonFileEqualsJsonFile' => true,
+        'assertJsonFileNotEqualsJsonFile' => true,
+        'assertJsonStringEqualsJsonFile' => true,
+        'assertJsonStringEqualsJsonString' => true,
+        'assertJsonStringNotEqualsJsonFile' => true,
+        'assertJsonStringNotEqualsJsonString' => true,
+        'assertLessThan' => true,
+        'assertLessThanOrEqual' => true,
+        'assertNan' => true,
+        'assertNotContains' => true,
+        'assertNotContainsOnly' => true,
+        'assertNotCount' => true,
+        'assertNotEmpty' => true,
+        'assertNotEquals' => true,
+        'assertNotFalse' => true,
+        'assertNotInstanceOf' => true,
+        'assertNotInternalType' => true,
+        'assertNotIsReadable' => true,
+        'assertNotIsWritable' => true,
+        'assertNotNull' => true,
+        'assertNotRegExp' => true,
+        'assertNotSame' => true,
+        'assertNotSameSize' => true,
+        'assertNotTrue' => true,
+        'assertNull' => true,
+        'assertObjectHasAttribute' => true,
+        'assertObjectNotHasAttribute' => true,
+        'assertRegExp' => true,
+        'assertSame' => true,
+        'assertSameSize' => true,
+        'assertStringEndsNotWith' => true,
+        'assertStringEndsWith' => true,
+        'assertStringEqualsFile' => true,
+        'assertStringMatchesFormat' => true,
+        'assertStringMatchesFormatFile' => true,
+        'assertStringNotEqualsFile' => true,
+        'assertStringNotMatchesFormat' => true,
+        'assertStringNotMatchesFormatFile' => true,
+        'assertStringStartsNotWith' => true,
+        'assertStringStartsWith' => true,
+        'assertThat' => true,
+        'assertTrue' => true,
+        'assertXmlFileEqualsXmlFile' => true,
+        'assertXmlFileNotEqualsXmlFile' => true,
+        'assertXmlStringEqualsXmlFile' => true,
+        'assertXmlStringEqualsXmlString' => true,
+        'assertXmlStringNotEqualsXmlFile' => true,
+        'assertXmlStringNotEqualsXmlString' => true,
+        'attribute' => true,
+        'attributeEqualTo' => true,
+        'callback' => true,
+        'classHasAttribute' => true,
+        'classHasStaticAttribute' => true,
+        'contains' => true,
+        'containsOnly' => true,
+        'containsOnlyInstancesOf' => true,
+        'countOf' => true,
+        'directoryExists' => true,
+        'equalTo' => true,
+        'fail' => true,
+        'fileExists' => true,
+        'getCount' => true,
+        'getObjectAttribute' => true,
+        'getStaticAttribute' => true,
+        'greaterThan' => true,
+        'greaterThanOrEqual' => true,
+        'identicalTo' => true,
+        'isEmpty' => true,
+        'isFalse' => true,
+        'isFinite' => true,
+        'isInfinite' => true,
+        'isInstanceOf' => true,
+        'isJson' => true,
+        'isNan' => true,
+        'isNull' => true,
+        'isReadable' => true,
+        'isTrue' => true,
+        'isType' => true,
+        'isWritable' => true,
+        'lessThan' => true,
+        'lessThanOrEqual' => true,
+        'logicalAnd' => true,
+        'logicalNot' => true,
+        'logicalOr' => true,
+        'logicalXor' => true,
+        'markTestIncomplete' => true,
+        'markTestSkipped' => true,
+        'matches' => true,
+        'matchesRegularExpression' => true,
+        'objectHasAttribute' => true,
+        'readAttribute' => true,
+        'resetCount' => true,
+        'stringContains' => true,
+        'stringEndsWith' => true,
+        'stringStartsWith' => true,
+
+        // TestCase methods
+        'any' => true,
+        'at' => true,
+        'atLeast' => true,
+        'atLeastOnce' => true,
+        'atMost' => true,
+        'exactly' => true,
+        'never' => true,
+        'onConsecutiveCalls' => true,
+        'once' => true,
+        'returnArgument' => true,
+        'returnCallback' => true,
+        'returnSelf' => true,
+        'returnValue' => true,
+        'returnValueMap' => true,
+        'setUpBeforeClass' => true,
+        'tearDownAfterClass' => true,
+        'throwException' => true,
+    ];
+
+    private $conversionMap = [
+        self::CALL_TYPE_THIS => [[T_OBJECT_OPERATOR, '->'], [T_VARIABLE, '$this']],
+        self::CALL_TYPE_SELF => [[T_DOUBLE_COLON, '::'], [T_STRING, 'self']],
+        self::CALL_TYPE_STATIC => [[T_DOUBLE_COLON, '::'], [T_STATIC, 'static']],
+    ];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition()
+    {
+        return new FixerDefinition(
+            'Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type, either `$this->`, `self::` or `static::`.',
+            [
+                new CodeSample(
+                    '<?php
+final class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testMe()
+    {
+        $this->assertSame(1, 2);
+        self::assertSame(1, 2);
+        static::assertSame(1, 2);
+    }
+}
+'
+                ),
+            ],
+            null,
+            'Risky when PHPUnit methods are overridden or not accessible, or when project has PHPUnit incompatibilities.'
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens)
+    {
+        return $tokens->isAllTokenKindsFound([T_CLASS, T_STRING]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isRisky()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
+    {
+        $phpUnitTestCaseIndicator = new PhpUnitTestCaseIndicator();
+        foreach (array_reverse(iterator_to_array($phpUnitTestCaseIndicator->findPhpUnitClasses($tokens))) as $indexes) {
+            $this->fixPhpUnitClass($tokens, $indexes[0], $indexes[1]);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function createConfigurationDefinition()
+    {
+        $thisFixer = $this;
+
+        return new FixerConfigurationResolver([
+            (new FixerOptionBuilder('call_type', 'The call type to use for referring to PHPUnit methods.'))
+                ->setAllowedTypes(['string'])
+                ->setAllowedValues(array_keys($this->allowedValues))
+                ->setDefault('static')
+                ->getOption(),
+            (new FixerOptionBuilder('methods', 'Dictionary of `method` => `call_type` values that differ from the default strategy.'))
+                ->setAllowedTypes(['array'])
+                ->setAllowedValues([static function ($option) use ($thisFixer) {
+                    foreach ($option as $method => $value) {
+                        if (!isset($thisFixer->staticMethods[$method])) {
+                            throw new InvalidOptionsException(
+                                sprintf(
+                                    'Unexpected "methods" key, expected any of "%s", got "%s".',
+                                    implode('", "', array_keys($thisFixer->staticMethods)),
+                                    is_object($method) ? get_class($method) : gettype($method).'#'.$method
+                                )
+                            );
+                        }
+
+                        if (!isset($thisFixer->allowedValues[$value])) {
+                            throw new InvalidOptionsException(
+                                sprintf(
+                                    'Unexpected value for method "%s", expected any of "%s", got "%s".',
+                                    $method,
+                                    implode('", "', array_keys($thisFixer->allowedValues)),
+                                    is_object($value) ? get_class($value) : (null === $value ? 'null' : gettype($value).'#'.$value)
+                                )
+                            );
+                        }
+                    }
+
+                    return true;
+                }])
+                ->setDefault([])
+                ->getOption(),
+        ]);
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param int    $startIndex
+     * @param int    $endIndex
+     */
+    private function fixPhpUnitClass(Tokens $tokens, $startIndex, $endIndex)
+    {
+        $analyzer = new TokensAnalyzer($tokens);
+
+        for ($index = $endIndex - 1; $index > $startIndex; --$index) {
+            if (!$tokens[$index]->isGivenKind(T_STRING) || !isset($this->staticMethods[$tokens[$index]->getContent()])) {
+                continue;
+            }
+
+            $methodName = $tokens[$index]->getContent();
+            $callType = $this->configuration['call_type'];
+            if (isset($this->configuration['methods'][$methodName])) {
+                $callType = $this->configuration['methods'][$methodName];
+            }
+
+            $operatorIndex = $tokens->getPrevMeaningfulToken($index);
+            $referenceIndex = $tokens->getPrevMeaningfulToken($operatorIndex);
+            if (!$this->needsConversion($tokens, $operatorIndex, $referenceIndex, $callType)) {
+                continue;
+            }
+
+            if (self::CALL_TYPE_THIS === $callType && $this->isInsideStaticFunction($tokens, $analyzer, $index)) {
+                continue;
+            }
+
+            $tokens[$operatorIndex] = new Token($this->conversionMap[$callType][0]);
+            $tokens[$referenceIndex] = new Token($this->conversionMap[$callType][1]);
+        }
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param int    $operatorIndex
+     * @param int    $referenceIndex
+     * @param string $callType
+     *
+     * @return bool
+     */
+    private function needsConversion(Tokens $tokens, $operatorIndex, $referenceIndex, $callType)
+    {
+        if ($tokens[$operatorIndex]->equals([T_DOUBLE_COLON, '::']) && $tokens[$referenceIndex]->equals([T_STATIC, 'static'])) {
+            return self::CALL_TYPE_STATIC !== $callType;
+        }
+
+        if ($tokens[$operatorIndex]->equals([T_OBJECT_OPERATOR, '->']) && $tokens[$referenceIndex]->equals([T_VARIABLE, '$this'])) {
+            return self::CALL_TYPE_THIS !== $callType;
+        }
+
+        if ($tokens[$operatorIndex]->equals([T_DOUBLE_COLON, '::']) && $tokens[$referenceIndex]->equals([T_STRING, 'self'])) {
+            return self::CALL_TYPE_SELF !== $callType;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param Tokens         $tokens
+     * @param TokensAnalyzer $analyzer
+     * @param int            $index
+     *
+     * @return bool
+     */
+    private function isInsideStaticFunction(Tokens $tokens, TokensAnalyzer $analyzer, $index)
+    {
+        $functionIndex = $tokens->getPrevTokenOfKind($index, [[T_FUNCTION]]);
+        while ($analyzer->isLambda($functionIndex)) {
+            $openingCurlyBraceIndex = $tokens->getNextTokenOfKind($functionIndex, ['{']);
+            $closingCurlyBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openingCurlyBraceIndex);
+
+            if ($closingCurlyBraceIndex > $index) {
+                $prev = $tokens->getPrevMeaningfulToken($functionIndex);
+                if ($tokens[$prev]->isGivenKind(T_STATIC)) {
+                    return true;
+                }
+            }
+
+            $functionIndex = $tokens->getPrevTokenOfKind($functionIndex, [[T_FUNCTION]]);
+        }
+
+        $prev = $functionIndex;
+        do {
+            $prev = $tokens->getPrevMeaningfulToken($prev);
+        } while ($tokens[$prev]->isGivenKind([T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL]));
+
+        return $tokens[$prev]->isGivenKind(T_STATIC);
+    }
+}

+ 6 - 6
tests/Console/Command/SelfUpdateCommandTest.php

@@ -77,7 +77,7 @@ final class SelfUpdateCommandTest extends TestCase
         $application = new Application();
         $application->add($command);
 
-        self::assertSame($command, $application->find($name));
+        $this->assertSame($command, $application->find($name));
     }
 
     public function provideCommandNameCases()
@@ -136,9 +136,9 @@ final class SelfUpdateCommandTest extends TestCase
 
         $commandTester = $this->execute($command, $input, $decorated);
 
-        self::assertSame($expectedFileContents, file_get_contents($this->getToolPath()));
+        $this->assertSame($expectedFileContents, file_get_contents($this->getToolPath()));
         $this->assertDisplay($expectedDisplay, $commandTester);
-        self::assertSame(0, $commandTester->getStatusCode());
+        $this->assertSame(0, $commandTester->getStatusCode());
     }
 
     public function provideExecuteCases()
@@ -280,7 +280,7 @@ OUTPUT;
             "\033[37;41mUnable to determine newest version: Foo.\033[39;49m\n",
             $commandTester
         );
-        self::assertSame(1, $commandTester->getStatusCode());
+        $this->assertSame(1, $commandTester->getStatusCode());
     }
 
     public function provideExecuteWhenNotAbleToGetLatestVersionsCases()
@@ -327,7 +327,7 @@ OUTPUT;
             "\033[37;41mSelf-update is available only for PHAR version.\033[39;49m\n",
             $commandTester
         );
-        self::assertSame(1, $commandTester->getStatusCode());
+        $this->assertSame(1, $commandTester->getStatusCode());
     }
 
     public function provideExecuteWhenNotInstalledAsPharCases()
@@ -372,7 +372,7 @@ OUTPUT;
             return preg_replace("/\033\\[39(;49)?m/", "\033[0m", $display);
         };
 
-        self::assertSame(
+        $this->assertSame(
             $cleanDisplay($expectedDisplay),
             $cleanDisplay($commandTester->getDisplay(true))
         );

+ 1 - 1
tests/Fixer/Alias/NoAliasFunctionsFixerTest.php

@@ -38,7 +38,7 @@ final class NoAliasFunctionsFixerTest extends AbstractFixerTestCase
     public function provideFixCases()
     {
         /** @var $aliases string[] */
-        $aliases = static::getStaticAttribute(\PhpCsFixer\Fixer\Alias\NoAliasFunctionsFixer::class, 'aliases');
+        $aliases = $this->getStaticAttribute(\PhpCsFixer\Fixer\Alias\NoAliasFunctionsFixer::class, 'aliases');
 
         $cases = [];
         foreach ($aliases as $alias => $master) {

+ 2 - 2
tests/Fixer/Alias/RandomApiMigrationFixerTest.php

@@ -52,7 +52,7 @@ final class RandomApiMigrationFixerTest extends AbstractFixerTestCase
             ['replacements' => [
                 'rand' => ['alternativeName' => 'random_int', 'argumentCount' => [0, 2]], ],
             ],
-            static::getObjectAttribute($this->fixer, 'configuration')
+            $this->getObjectAttribute($this->fixer, 'configuration')
         );
     }
 
@@ -64,7 +64,7 @@ final class RandomApiMigrationFixerTest extends AbstractFixerTestCase
             ['replacements' => [
                 'rand' => ['alternativeName' => 'random_int', 'argumentCount' => [0, 2]], ],
             ],
-            static::getObjectAttribute($this->fixer, 'configuration')
+            $this->getObjectAttribute($this->fixer, 'configuration')
         );
     }
 

+ 2 - 2
tests/Fixer/LanguageConstruct/IsNullFixerTest.php

@@ -52,8 +52,8 @@ final class IsNullFixerTest extends AbstractFixerTestCase
     {
         $this->fixer->configure(['use_yoda_style' => false]);
 
-        $configuration = static::getObjectAttribute($this->fixer, 'configuration');
-        static::assertFalse($configuration['use_yoda_style']);
+        $configuration = $this->getObjectAttribute($this->fixer, 'configuration');
+        $this->assertFalse($configuration['use_yoda_style']);
     }
 
     /**

+ 438 - 0
tests/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixerTest.php

@@ -0,0 +1,438 @@
+<?php
+
+/*
+ * 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\Fixer\PhpUnit\PhpUnitTestCaseStaticMethodCallsFixer;
+use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @author Filippo Tessarotto <zoeslam@gmail.com>
+ *
+ * @internal
+ *
+ * @covers \PhpCsFixer\Fixer\PhpUnit\PhpUnitTestCaseStaticMethodCallsFixer
+ */
+final class PhpUnitTestCaseStaticMethodCallsFixerTest extends AbstractFixerTestCase
+{
+    public function testFixerContainsAllPhpunitStaticMethodsInItsList()
+    {
+        $assertionRefClass = new \ReflectionClass(TestCase::class);
+        $updatedStaticMethodsList = $assertionRefClass->getMethods(\ReflectionMethod::IS_PUBLIC);
+
+        $fixerRefClass = new \ReflectionClass(PhpUnitTestCaseStaticMethodCallsFixer::class);
+        $defaultProperties = $fixerRefClass->getDefaultProperties();
+        $staticMethods = $defaultProperties['staticMethods'];
+
+        $missingMethods = [];
+        foreach ($updatedStaticMethodsList as $method) {
+            if ($method->isStatic() && !isset($staticMethods[$method->name])) {
+                $missingMethods[] = $method->name;
+            }
+        }
+
+        $this->assertSame([], $missingMethods, sprintf('The following static methods from "%s" are missing from "%s::$staticMethods"', TestCase::class, PhpUnitTestCaseStaticMethodCallsFixer::class));
+    }
+
+    public function testWrongConfigTypeForMethodsKey()
+    {
+        $this->expectException(\PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException::class);
+        $this->expectExceptionMessageRegExp('/Unexpected "methods" key, expected any of ".*", got "integer#123"\.$/');
+
+        $this->fixer->configure(['methods' => [123 => 1]]);
+    }
+
+    public function testWrongConfigTypeForMethodsValue()
+    {
+        $this->expectException(\PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException::class);
+        $this->expectExceptionMessageRegExp('/Unexpected value for method "assertSame", expected any of ".*", got "integer#123"\.$/');
+
+        $this->fixer->configure(['methods' => ['assertSame' => 123]]);
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     * @param array       $config
+     *
+     * @dataProvider provideTestFixCases
+     */
+    public function testFix($expected, $input = null, array $config = [])
+    {
+        $this->fixer->configure($config);
+        $this->doTest($expected, $input);
+    }
+
+    public function provideTestFixCases()
+    {
+        return [
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        static::assertSame(1, 2);
+        static::markTestIncomplete('foo');
+        static::fail('foo');
+    }
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        $this->assertSame(1, 2);
+        $this->markTestIncomplete('foo');
+        $this->fail('foo');
+    }
+}
+EOF
+                ,
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testMocks()
+    {
+        $mock = $this->createMock(MyInterface::class);
+        $mock
+            ->expects(static::once())
+            ->method('run')
+            ->with(
+                static::identicalTo(1),
+                static::stringContains('foo')
+            )
+            ->will(static::onConsecutiveCalls(
+                static::returnSelf(),
+                static::throwException(new \Exception())
+            ))
+        ;
+    }
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testMocks()
+    {
+        $mock = $this->createMock(MyInterface::class);
+        $mock
+            ->expects($this->once())
+            ->method('run')
+            ->with(
+                $this->identicalTo(1),
+                $this->stringContains('foo')
+            )
+            ->will($this->onConsecutiveCalls(
+                $this->returnSelf(),
+                $this->throwException(new \Exception())
+            ))
+        ;
+    }
+}
+EOF
+                ,
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testWeirdIndentation()
+    {
+        static
+        // @TODO
+            ::
+        assertSame
+        (1, 2);
+        // $this->markTestIncomplete('foo');
+        /*
+        $this->fail('foo');
+        */
+    }
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testWeirdIndentation()
+    {
+        $this
+        // @TODO
+            ->
+        assertSame
+        (1, 2);
+        // $this->markTestIncomplete('foo');
+        /*
+        $this->fail('foo');
+        */
+    }
+}
+EOF
+                ,
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        $this->assertSame(1, 2);
+        $this->markTestIncomplete('foo');
+        $this->fail('foo');
+
+        $lambda = function () {
+            $this->assertSame(1, 2);
+            $this->assertSame(1, 2);
+            $this->assertSame(1, 2);
+        };
+    }
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        $this->assertSame(1, 2);
+        self::markTestIncomplete('foo');
+        static::fail('foo');
+
+        $lambda = function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+    }
+}
+EOF
+                ,
+                ['call_type' => PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_THIS],
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        self::assertSame(1, 2);
+        self::markTestIncomplete('foo');
+        self::fail('foo');
+    }
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        $this->assertSame(1, 2);
+        self::markTestIncomplete('foo');
+        static::fail('foo');
+    }
+}
+EOF
+                ,
+                ['call_type' => PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_SELF],
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        $this->assertSame(1, 2);
+        $this->assertSame(1, 2);
+
+        static::setUpBeforeClass();
+        static::setUpBeforeClass();
+
+        $otherTest->setUpBeforeClass();
+        OtherTest::setUpBeforeClass();
+    }
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function testBaseCase()
+    {
+        static::assertSame(1, 2);
+        $this->assertSame(1, 2);
+
+        static::setUpBeforeClass();
+        $this->setUpBeforeClass();
+
+        $otherTest->setUpBeforeClass();
+        OtherTest::setUpBeforeClass();
+    }
+}
+EOF
+                ,
+                [
+                    'call_type' => PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_THIS,
+                    'methods' => ['setUpBeforeClass' => PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_STATIC],
+                ],
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public static function foo()
+    {
+        $this->assertSame(1, 2);
+        self::assertSame(1, 2);
+        static::assertSame(1, 2);
+
+        $lambda = function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+    }
+
+    public function bar()
+    {
+        $lambda = static function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+    }
+
+    static public function baz()
+    {
+        $this->assertSame(1, 2);
+        self::assertSame(1, 2);
+        static::assertSame(1, 2);
+
+        $lambda = function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+    }
+
+    static final protected function xyz()
+    {
+        static::assertSame(1, 2);
+    }
+}
+EOF
+                ,
+                null,
+                [
+                    'call_type' => PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_THIS,
+                ],
+            ],
+            [
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function foo()
+    {
+        $this->assertSame(1, 2);
+        $this->assertSame(1, 2);
+        $this->assertSame(1, 2);
+    }
+
+    public function bar()
+    {
+        $lambdaOne = static function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+
+        $lambdaTwo = function () {
+            $this->assertSame(1, 2);
+            $this->assertSame(1, 2);
+            $this->assertSame(1, 2);
+        };
+    }
+
+    public function baz()
+    {
+        $this->assertSame(1, 2);
+        $this->assertSame(1, 2);
+        $this->assertSame(1, 2);
+    }
+
+}
+EOF
+                ,
+                <<<'EOF'
+<?php
+class MyTest extends \PHPUnit_Framework_TestCase
+{
+    public function foo()
+    {
+        $this->assertSame(1, 2);
+        self::assertSame(1, 2);
+        static::assertSame(1, 2);
+    }
+
+    public function bar()
+    {
+        $lambdaOne = static function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+
+        $lambdaTwo = function () {
+            $this->assertSame(1, 2);
+            self::assertSame(1, 2);
+            static::assertSame(1, 2);
+        };
+    }
+
+    public function baz()
+    {
+        $this->assertSame(1, 2);
+        self::assertSame(1, 2);
+        static::assertSame(1, 2);
+    }
+
+}
+EOF
+                ,
+                [
+                    'call_type' => PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_THIS,
+                ],
+            ],
+        ];
+    }
+}

+ 2 - 2
tests/FixerConfiguration/AllowedValueSubsetTest.php

@@ -24,7 +24,7 @@ final class AllowedValueSubsetTest extends TestCase
 {
     public function testConstructor()
     {
-        self::assertInternalType('callable', new AllowedValueSubset(['foo', 'bar']));
+        $this->assertInternalType('callable', new AllowedValueSubset(['foo', 'bar']));
     }
 
     /**
@@ -37,7 +37,7 @@ final class AllowedValueSubsetTest extends TestCase
     {
         $subset = new AllowedValueSubset(['foo', 'bar']);
 
-        self::assertSame($expectedResult, $subset($inputValue));
+        $this->assertSame($expectedResult, $subset($inputValue));
     }
 
     public function provideInvokeCases()