Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: phpstan/phpstan-phpunit
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2.0.4
Choose a base ref
...
head repository: phpstan/phpstan-phpunit
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2.0.5
Choose a head ref
  • 13 commits
  • 25 files changed
  • 4 contributors

Commits on Jan 28, 2025

  1. Update LICENSE

    ondrejmirtes authored Jan 28, 2025
    Copy the full SHA
    17bbfd3 View commit details

Commits on Mar 18, 2025

  1. Introduce phpstan-deprecation-rules

    staabm authored Mar 18, 2025
    Copy the full SHA
    0f857bf View commit details

Commits on Mar 23, 2025

  1. Remove config.platform

    ondrejmirtes committed Mar 23, 2025
    Copy the full SHA
    855b82c View commit details
  2. Copy the full SHA
    846d161 View commit details
  3. Copy the full SHA
    342b6c1 View commit details
  4. Remove deprecated assert

    ondrejmirtes committed Mar 23, 2025
    Copy the full SHA
    630aa99 View commit details
  5. Copy the full SHA
    19f8059 View commit details
  6. Always install nikic/php-parser v5

    ondrejmirtes committed Mar 23, 2025
    Copy the full SHA
    eb88670 View commit details
  7. Add non regression test for #222

    VincentLanglet authored Mar 23, 2025
    Copy the full SHA
    1a07095 View commit details

Commits on Mar 24, 2025

  1. chore(deps): update metcalfc/changelog-generator action to v4.5.0

    renovate[bot] authored and ondrejmirtes committed Mar 24, 2025
    Copy the full SHA
    40fbbc1 View commit details
  2. Test PHPUnit v12

    ondrejmirtes committed Mar 24, 2025
    Copy the full SHA
    bf031ae View commit details
  3. Copy the full SHA
    1f36fc5 View commit details
  4. InvocationMocker class no longer exists

    ondrejmirtes committed Mar 24, 2025
    Copy the full SHA
    4d2b44b View commit details
54 changes: 54 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -97,6 +97,30 @@ jobs:
dependencies:
- "lowest"
- "highest"
phpunit-version:
- "^9.5"
- "^10.5"
- "^11.5"
- "^12.0.9"
exclude:
- php-version: "7.4"
phpunit-version: "^10.5"
- php-version: "8.0"
phpunit-version: "^10.5"
- php-version: "7.4"
phpunit-version: "^11.5"
- php-version: "8.0"
phpunit-version: "^11.5"
- php-version: "8.1"
phpunit-version: "^11.5"
- php-version: "7.4"
phpunit-version: "^12.0.9"
- php-version: "8.0"
phpunit-version: "^12.0.9"
- php-version: "8.1"
phpunit-version: "^12.0.9"
- php-version: "8.2"
phpunit-version: "^12.0.9"

steps:
- name: "Checkout"
@@ -108,6 +132,9 @@ jobs:
coverage: "none"
php-version: "${{ matrix.php-version }}"

- name: "Require specific PHPUnit version"
run: "composer require --dev phpunit/phpunit:${{ matrix.phpunit-version }}"

- name: "Install lowest dependencies"
if: ${{ matrix.dependencies == 'lowest' }}
run: "composer update --prefer-lowest --no-interaction --no-progress"
@@ -136,6 +163,30 @@ jobs:
dependencies:
- "lowest"
- "highest"
phpunit-version:
- "^9.5"
- "^10.5"
- "^11.5"
- "^12.0.9"
exclude:
- php-version: "7.4"
phpunit-version: "^10.5"
- php-version: "8.0"
phpunit-version: "^10.5"
- php-version: "7.4"
phpunit-version: "^11.5"
- php-version: "8.0"
phpunit-version: "^11.5"
- php-version: "8.1"
phpunit-version: "^11.5"
- php-version: "7.4"
phpunit-version: "^12.0.9"
- php-version: "8.0"
phpunit-version: "^12.0.9"
- php-version: "8.1"
phpunit-version: "^12.0.9"
- php-version: "8.2"
phpunit-version: "^12.0.9"

steps:
- name: "Checkout"
@@ -149,6 +200,9 @@ jobs:
extensions: mbstring
tools: composer:v2

- name: "Require specific PHPUnit version"
run: "composer require --dev phpunit/phpunit:${{ matrix.phpunit-version }}"

- name: "Install lowest dependencies"
if: ${{ matrix.dependencies == 'lowest' }}
run: "composer update --prefer-lowest --no-interaction --no-progress"
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ jobs:

- name: Generate changelog
id: changelog
uses: metcalfc/changelog-generator@v4.3.1
uses: metcalfc/changelog-generator@v4.5.0
with:
myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }}

1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2016 Ondřej Mirtes
Copyright (c) 2025 PHPStan s.r.o.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
5 changes: 2 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -13,14 +13,13 @@
"phpunit/phpunit": "<7.0"
},
"require-dev": {
"nikic/php-parser": "^5",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.6"
},
"config": {
"platform": {
"php": "7.4.6"
},
"sort-packages": true
},
"extra": {
9 changes: 0 additions & 9 deletions extension.neon
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ parameters:
- stubs/Assert.stub
- stubs/AssertionFailedError.stub
- stubs/ExpectationFailedException.stub
- stubs/InvocationMocker.stub
- stubs/MockBuilder.stub
- stubs/MockObject.stub
- stubs/Stub.stub
@@ -42,18 +41,10 @@ services:
class: PHPStan\Type\PHPUnit\Assert\AssertStaticMethodTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension
-
class: PHPStan\Type\PHPUnit\InvocationMockerDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Type\PHPUnit\MockBuilderDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Type\PHPUnit\MockObjectDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Rules\PHPUnit\CoversHelper
-
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -14,3 +14,8 @@ parameters:
message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
count: 1
path: tests/Rules/PHPUnit/ImpossibleCheckTypeMethodCallRuleTest.php

-
message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#"
count: 1
path: tests/Rules/Methods/CallMethodsRuleTest.php
6 changes: 6 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -2,9 +2,15 @@ includes:
- extension.neon
- rules.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
- phar://phpstan.phar/conf/bleedingEdge.neon
- phpstan-baseline.neon

parameters:
excludePaths:
- tests/*/data/*
ignoreErrors:
-
message: '#^Attribute class PHPUnit\\Framework\\Attributes\\DataProvider does not exist\.$#'
identifier: attribute.notFound
reportUnmatched: false
14 changes: 0 additions & 14 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -12,20 +12,6 @@
failOnWarning="true"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xml"
>
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
<report>
<clover outputFile="tests/tmp/clover.xml"/>
<text
outputFile="php://stdout"
showUncoveredFiles="true"
showOnlySummary="true"
/>
</report>
</coverage>

<testsuites>
<testsuite name="PHPStan for PHPUnit">
<directory suffix="Test.php">tests</directory>
2 changes: 1 addition & 1 deletion src/Rules/PHPUnit/ClassCoversExistsRule.php
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ public function processNode(Node $node, Scope $scope): array
{
$classReflection = $node->getClassReflection();

if (!$classReflection->isSubclassOf(TestCase::class)) {
if (!$classReflection->is(TestCase::class)) {
return [];
}

2 changes: 1 addition & 1 deletion src/Rules/PHPUnit/ClassMethodCoversExistsRule.php
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if (!$classReflection->isSubclassOf(TestCase::class)) {
if (!$classReflection->is(TestCase::class)) {
return [];
}

2 changes: 1 addition & 1 deletion src/Rules/PHPUnit/DataProviderDeclarationRule.php
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ public function processNode(Node $node, Scope $scope): array
{
$classReflection = $scope->getClassReflection();

if ($classReflection === null || !$classReflection->isSubclassOf(TestCase::class)) {
if ($classReflection === null || !$classReflection->is(TestCase::class)) {
return [];
}

6 changes: 3 additions & 3 deletions src/Rules/PHPUnit/DataProviderHelper.php
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ public function getDataProviderMethods(
}

$dataProviderMethod = $this->parseDataProviderAnnotationValue($scope, $dataProviderValue);
$dataProviderMethod[] = $node->getLine();
$dataProviderMethod[] = $node->getStartLine();

yield $dataProviderValue => $dataProviderMethod;
}
@@ -257,7 +257,7 @@ private function parseDataProviderExternalAttribute(Attribute $attribute): ?arra
sprintf('%s::%s', $className, $methodNameArg->value) => [
$dataProviderClassReflection,
$methodNameArg->value,
$attribute->getLine(),
$attribute->getStartLine(),
],
];
}
@@ -279,7 +279,7 @@ private function parseDataProviderAttribute(Attribute $attribute, ClassReflectio
$methodNameArg->value => [
$classReflection,
$methodNameArg->value,
$attribute->getLine(),
$attribute->getStartLine(),
],
];
}
65 changes: 40 additions & 25 deletions src/Rules/PHPUnit/MockMethodCallRule.php
Original file line number Diff line number Diff line change
@@ -5,9 +5,10 @@
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPUnit\Framework\MockObject\Builder\InvocationMocker;
use PHPStan\Type\Type;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Stub;
use function array_filter;
@@ -47,44 +48,58 @@ public function processNode(Node $node, Scope $scope): array
$method = $constantString->getValue();
$type = $scope->getType($node->var);

if (
(
in_array(MockObject::class, $type->getObjectClassNames(), true)
|| in_array(Stub::class, $type->getObjectClassNames(), true)
)
&& !$type->hasMethod($method)->yes()
) {
$mockClasses = array_filter($type->getObjectClassNames(), static fn (string $class): bool => $class !== MockObject::class && $class !== Stub::class);
if (count($mockClasses) === 0) {
continue;
}

$errors[] = RuleErrorBuilder::message(sprintf(
'Trying to mock an undefined method %s() on class %s.',
$method,
implode('&', $mockClasses),
))->identifier('phpunit.mockMethod')->build();
$error = $this->checkCallOnType($type, $method);
if ($error !== null) {
$errors[] = $error;
continue;
}

$mockedClassObject = $type->getTemplateType(InvocationMocker::class, 'TMockedClass');
if ($mockedClassObject->hasMethod($method)->yes()) {
if (!$node->var instanceof MethodCall) {
continue;
}

$classNames = $mockedClassObject->getObjectClassNames();
if (count($classNames) === 0) {
if (!$node->var->name instanceof Node\Identifier) {
continue;
}

$errors[] = RuleErrorBuilder::message(sprintf(
if ($node->var->name->toLowerString() !== 'expects') {
continue;
}

$varType = $scope->getType($node->var->var);
$error = $this->checkCallOnType($varType, $method);
if ($error === null) {
continue;
}

$errors[] = $error;
}

return $errors;
}

private function checkCallOnType(Type $type, string $method): ?IdentifierRuleError
{
if (
(
in_array(MockObject::class, $type->getObjectClassNames(), true)
|| in_array(Stub::class, $type->getObjectClassNames(), true)
)
&& !$type->hasMethod($method)->yes()
) {
$mockClasses = array_filter($type->getObjectClassNames(), static fn (string $class): bool => $class !== MockObject::class && $class !== Stub::class);
if (count($mockClasses) === 0) {
return null;
}

return RuleErrorBuilder::message(sprintf(
'Trying to mock an undefined method %s() on class %s.',
$method,
implode('|', $classNames),
implode('&', $mockClasses),
))->identifier('phpunit.mockMethod')->build();
}

return $errors;
return null;
}

}
2 changes: 1 addition & 1 deletion src/Rules/PHPUnit/NoMissingSpaceInClassAnnotationRule.php
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ public function getNodeType(): string
public function processNode(Node $node, Scope $scope): array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || $classReflection->isSubclassOf(TestCase::class) === false) {
if ($classReflection === null || $classReflection->is(TestCase::class) === false) {
return [];
}

2 changes: 1 addition & 1 deletion src/Rules/PHPUnit/NoMissingSpaceInMethodAnnotationRule.php
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ public function getNodeType(): string
public function processNode(Node $node, Scope $scope): array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || $classReflection->isSubclassOf(TestCase::class) === false) {
if ($classReflection === null || $classReflection->is(TestCase::class) === false) {
return [];
}

2 changes: 1 addition & 1 deletion src/Rules/PHPUnit/ShouldCallParentMethodsRule.php
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if (!$scope->getClassReflection()->isSubclassOf(TestCase::class)) {
if (!$scope->getClassReflection()->is(TestCase::class)) {
return [];
}

30 changes: 0 additions & 30 deletions src/Type/PHPUnit/InvocationMockerDynamicReturnTypeExtension.php

This file was deleted.

Loading