Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 164 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,18 @@ includes:

<br>

Do you use mocks in your PHPUnit tests? Enable mocking rules with single parameter:

```yaml
parameters:
mocks: true
```

<br>

But at start, make baby steps with one rule at a time:

Jump to: [Symfony-specific rules](#3-symfony-specific-rules), [Doctrine-specific rules](#2-doctrine-specific-rules) or [PHPUnit-specific rules](#4-phpunit-specific-rules).
Jump to: [Symfony-specific rules](#3-symfony-specific-rules), [Doctrine-specific rules](#2-doctrine-specific-rules), [PHPUnit-specific rules](#4-phpunit-specific-rules) or [PHPUnit mock rules](#5-phpunit-mock-rules).

<br>

Expand Down Expand Up @@ -2601,35 +2610,42 @@ return function (ContainerConfigurator $container) {

## 4. PHPUnit-specific Rules

### NoMockObjectAndRealObjectPropertyRule
### NoAssertFuncCallInTestsRule

Avoid using one property for both real object and mock object. Use separate properties or single type instead
Avoid using assert*() functions in tests, as they can lead to false positives

```yaml
rules:
- Symplify\PHPStanRules\Rules\PHPUnit\NoMockObjectAndRealObjectPropertyRule
- Symplify\PHPStanRules\Rules\PHPUnit\NoAssertFuncCallInTestsRule
```

<br>

### NoEntityMockingRule, NoDocumentMockingRule
### PublicStaticDataProviderRule

Instead of entity or document mocking, create object directly to get better type support
PHPUnit data provider method "%s" must be public

```yaml
rules:
- Symplify\PHPStanRules\Rules\Doctrine\NoEntityMockingRule
- Symplify\PHPStanRules\Rules\Doctrine\NoDocumentMockingRule
- Symplify\PHPStanRules\Rules\PHPUnit\PublicStaticDataProviderRule
```

```php
use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
public function test()
/**
* @dataProvider dataProvider
*/
public function test(): array
{
$someEntityMock = $this->createMock(SomeEntity::class);
return [];
}

protected function dataProvider(): array
{
return [];
}
}
```
Expand All @@ -2643,9 +2659,17 @@ use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
public function test()
/**
* @dataProvider dataProvider
*/
public function test(): array
{
$someEntityMock = new SomeEntity();
return [];
}

public static function dataProvider(): array
{
return [];
}
}
```
Expand All @@ -2654,13 +2678,17 @@ final class SomeTest extends TestCase

<br>

### NoAssertFuncCallInTestsRule
## 5. PHPUnit Mock Rules

Avoid using assert*() functions in tests, as they can lead to false positives
* Do you use extensive mocking in your PHPUnit tests?
* Do you want to keep your tests clean, maintainable and avoid upgrade hell in the future?
* Do you want to have tests that actually test something?

This set is for you! Enable all mocking rules with single parameter in your `phpstan.neon`:

```yaml
rules:
- Symplify\PHPStanRules\Rules\PHPUnit\NoAssertFuncCallInTestsRule
parameters:
mocks: true
```

<br>
Expand All @@ -2669,11 +2697,6 @@ rules:

Test should have at least one non-mocked property, to test something

```yaml
rules:
- Symplify\PHPStanRules\Rules\PHPUnit\NoMockOnlyTestRule
```

```php
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -2717,31 +2740,47 @@ class SomeTest extends TestCase

<br>

### PublicStaticDataProviderRule
### NoMockObjectAndRealObjectPropertyRule

PHPUnit data provider method "%s" must be public
Avoid using one property for both real object and mock object. Use separate properties or single type instead

```yaml
rules:
- Symplify\PHPStanRules\Rules\PHPUnit\PublicStaticDataProviderRule
```php
$this->service = $this->createMock(Service::class);
$this->service = new Service();
```

:x:

<br>

```php
$this->someMock = $this->createMock(AnotherService::class);

$this->realService = new Service();
```

:+1:

<br>

### NoDoubleConsecutiveTestMockRule

Do not use `willReturnOnConsecutiveCalls()` and `willReturnCallback()` on the same mock. Use `willReturnCallback()` only instead to make the test more clear.

```php
use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
/**
* @dataProvider dataProvider
*/
public function test(): array
{
return [];
}

protected function dataProvider(): array
public function test()
{
return [];
$this->createMock('SomeClass')
->expects($this->exactly(2))
->method('someMethod')
->willReturnCallback(function () {
return 'first';
})
->willReturnOnConsecutiveCalls('first');
}
}
```
Expand All @@ -2755,17 +2794,14 @@ use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
/**
* @dataProvider dataProvider
*/
public function test(): array
{
return [];
}

public static function dataProvider(): array
public function test()
{
return [];
$this->createMock('SomeClass')
->expects($this->exactly(2))
->method('someMethod')
->willReturnCallback(function () {
return 'first';
});
}
}
```
Expand All @@ -2776,12 +2812,7 @@ final class SomeTest extends TestCase

### ExplicitExpectsMockMethodRule

PHPUnit mock method is missing explicit `expects()`, e.g. `$this->mock->expects($this->once())->...`

```yaml
rules:
- Symplify\PHPStanRules\Rules\PHPUnit\ExplicitExpectsMockMethodRule
```
PHPUnit mock method is missing explicit `expects()`, e.g. `$this->mock->expects($this->once())->...`. This is required since PHPUnit 12 to avoid silent stubs.

```php
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -2820,29 +2851,97 @@ final class SomeTest extends TestCase

<br>

### NoDoubleConsecutiveTestMockRule
### AvoidAnyExpectsRule

Do not use `willReturnOnConsecutiveCalls()` and `willReturnCallback()` on the same mock. Use `willReturnCallback()` only instead to make the test more clear.
Disallow usage of `any()` expectation in mocks to ensure that all mock interactions are explicitly defined and verified.

```yaml
rules:
- Symplify\PHPStanRules\Rules\PHPUnit\NoDoubleConsecutiveTestMockRule
```php
$someMock = $this->createMock(Service::class);
$someMock->expects($this->any())
->method('calculate')
->willReturn(10);
```

:x:

<br>

```php
$someMock = $this->createMock(Service::class);
$someMock->expects($this->once())
->method('calculate')
->willReturn(10);
```

:+1:

<br>

### NoWithOnStubRule

Disallow `with()` on stubs (mocks without `expects()`). PHPUnit deprecates `with()` on test stubs because they silently swallow argument mismatches.

```php
$someMock = $this->createMock(Service::class);
$someMock->method('calculate')
->with(10)
->willReturn(20);
```

:x:

<br>

```php
$someMock = $this->createMock(Service::class);
$someMock->expects($this->once())
->method('calculate')
->with(10)
->willReturn(20);
```

:+1:

<br>

### RequireAtLeastOneRule

Disallow `atLeast(0)` on mock expectations, as it matches any number of calls (including zero) and provides no real verification. Require a value of `1` or higher.

```php
$someMock = $this->createMock(Service::class);
$someMock->expects($this->atLeast(0))
->method('calculate')
->willReturn(10);
```

:x:

<br>

```php
$someMock = $this->createMock(Service::class);
$someMock->expects($this->atLeast(1))
->method('calculate')
->willReturn(10);
```

:+1:

<br>

### NoEntityMockingRule, NoDocumentMockingRule

Instead of entity or document mocking, create object directly to get better type support

```php
use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
public function test()
{
$this->createMock('SomeClass')
->expects($this->exactly(2))
->method('someMethod')
->willReturnCallback(function () {
return 'first';
})
->willReturnOnConsecutiveCalls('first');
$someEntityMock = $this->createMock(SomeEntity::class);
}
}
```
Expand All @@ -2858,12 +2957,7 @@ final class SomeTest extends TestCase
{
public function test()
{
$this->createMock('SomeClass')
->expects($this->exactly(2))
->method('someMethod')
->willReturnCallback(function () {
return 'first';
});
$someEntityMock = new SomeEntity();
}
}
```
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
"phpstan": {
"includes": [
"config/services/services.neon",
"config/ctor-rules.neon"
"config/ctor-rules.neon",
"config/mock-rules.neon"
]
}
}
Expand Down
Loading
Loading