Skip to content

Commit ce9a314

Browse files
committed
AssertEqualsIsDiscouragedRule
1 parent 1187ed5 commit ce9a314

File tree

5 files changed

+163
-0
lines changed

5 files changed

+163
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ It also contains this strict framework-specific rules (can be enabled separately
2424
* Check that you are not using `assertSame()` with `false` as expected value. `assertFalse()` should be used instead.
2525
* Check that you are not using `assertSame()` with `null` as expected value. `assertNull()` should be used instead.
2626
* Check that you are not using `assertSame()` with `count($variable)` as second parameter. `assertCount($variable)` should be used instead.
27+
* Check that you are not using `assertEquals()` with same types (`assertSame()` should be used) or without explanatory comment above.
2728

2829
## How to document mock objects in phpDocs?
2930

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PHPUnit;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Type\BooleanType;
8+
use PHPStan\Type\FloatType;
9+
use PHPStan\Type\IntegerType;
10+
use PHPStan\Type\StringType;
11+
12+
class AssertEqualsIsDiscouragedRule implements \PHPStan\Rules\Rule
13+
{
14+
15+
public function getNodeType(): string
16+
{
17+
return \PhpParser\NodeAbstract::class;
18+
}
19+
20+
/**
21+
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $node
22+
* @param \PHPStan\Analyser\Scope $scope
23+
* @return string[] errors
24+
*/
25+
public function processNode(Node $node, Scope $scope): array
26+
{
27+
if (!AssertRuleHelper::isMethodOrStaticCallOnTestCase($node, $scope)) {
28+
return [];
29+
}
30+
31+
if (count($node->args) < 2) {
32+
return [];
33+
}
34+
if (!is_string($node->name) || strtolower($node->name) !== 'assertequals') {
35+
return [];
36+
}
37+
38+
$leftType = $scope->getType($node->args[0]->value);
39+
$rightType = $scope->getType($node->args[1]->value);
40+
41+
if (
42+
($leftType instanceof BooleanType && $rightType instanceof BooleanType)
43+
|| ($leftType instanceof IntegerType && $rightType instanceof IntegerType)
44+
|| ($leftType instanceof StringType && $rightType instanceof StringType)
45+
) {
46+
$typeDescription = $leftType->describe();
47+
if ($leftType instanceof BooleanType) {
48+
$typeDescription = 'bool';
49+
}
50+
return [
51+
sprintf(
52+
'You should use assertSame instead of assertEquals, because both values are of the same type "%s"',
53+
$typeDescription
54+
),
55+
];
56+
}
57+
if (
58+
($leftType instanceof FloatType && $rightType instanceof FloatType)
59+
&&
60+
count($node->args) < 4 // is not using delta for comparing floats
61+
) {
62+
return [
63+
'You should use assertSame instead of assertEquals, because both values are of the same type "float" and you are not using $delta argument',
64+
];
65+
}
66+
67+
$fileContents = explode("\n", file_get_contents($scope->getFile()));
68+
$previousLine = $fileContents[$node->getLine() - 2];
69+
70+
if (!preg_match('~^\s+//\s+assertEquals because(.*)~', $previousLine)) {
71+
return [
72+
'You should use assertSame instead of assertEquals. Or it should have a comment above with explanation: // assertEquals because ...',
73+
];
74+
}
75+
76+
return [];
77+
}
78+
79+
}

strictRules.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ services:
1111
class: PHPStan\Rules\PHPUnit\AssertSameWithCountRule
1212
tags:
1313
- phpstan.rules.rule
14+
-
15+
class: PHPStan\Rules\PHPUnit\AssertEqualsIsDiscouragedRule
16+
tags:
17+
- phpstan.rules.rule
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PHPUnit;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class AssertEqualsIsDiscouragedRuleTest extends \PHPStan\Testing\RuleTestCase
8+
{
9+
10+
protected function getRule(): Rule
11+
{
12+
return new AssertEqualsIsDiscouragedRule();
13+
}
14+
15+
public function testRule()
16+
{
17+
$this->analyse([__DIR__ . '/data/assert-equals-is-discouraged.php'], [
18+
[
19+
'You should use assertSame instead of assertEquals, because both values are of the same type "string"',
20+
11,
21+
],
22+
[
23+
'You should use assertSame instead of assertEquals, because both values are of the same type "int"',
24+
12,
25+
],
26+
[
27+
'You should use assertSame instead of assertEquals, because both values are of the same type "bool"',
28+
13,
29+
],
30+
[
31+
'You should use assertSame instead of assertEquals, because both values are of the same type "float" and you are not using $delta argument',
32+
16,
33+
],
34+
[
35+
'You should use assertSame instead of assertEquals. Or it should have a comment above with explanation: // assertEquals because ...',
36+
19,
37+
],
38+
[
39+
'You should use assertSame instead of assertEquals. Or it should have a comment above with explanation: // assertEquals because ...',
40+
21,
41+
],
42+
[
43+
'You should use assertSame instead of assertEquals. Or it should have a comment above with explanation: // assertEquals because ...',
44+
24,
45+
],
46+
]);
47+
}
48+
49+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace ExampleTestCase;
4+
5+
class AssertEqualsIsDiscouragedTestCase extends \PHPUnit\Framework\TestCase
6+
{
7+
8+
public function testAssertEqualsIsDiscouraged()
9+
{
10+
// assertSame can be used as both are of same type
11+
$this->assertEquals('a', 'b');
12+
$this->assertEquals(1, 2);
13+
$this->assertEquals(true, false);
14+
15+
// comparing floats without delta
16+
$this->assertEquals(1.0, 2.0);
17+
18+
// comparing floats with delta
19+
$this->assertEquals(1.0, 2.0, '', 0.01);
20+
21+
$this->assertEquals(1, '1'); // assertEquals without comment on previous line
22+
23+
// with incorrect comment
24+
$this->assertEquals(1, '1');
25+
26+
// assertEquals because I want it!
27+
$this->assertEquals(1, '1');
28+
}
29+
30+
}

0 commit comments

Comments
 (0)