PHPackages                             ikvasnica/phpstan-clean-test - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. ikvasnica/phpstan-clean-test

ActivePhpstan-extension[Testing &amp; Quality](/categories/testing)

ikvasnica/phpstan-clean-test
============================

PHPStan extension with opinionated strict rules for better code in tests.

0.4(2y ago)1520.1kMITPHPPHP &gt;=8.1

Since Jan 27Pushed 2y ago3 watchersCompare

[ Source](https://github.com/ikvasnica/phpstan-clean-test)[ Packagist](https://packagist.org/packages/ikvasnica/phpstan-clean-test)[ Docs](https://github.com/ikvasnica/phpstan-clean-test)[ RSS](/packages/ikvasnica-phpstan-clean-test/feed)WikiDiscussions master Synced today

READMEChangelog (6)Dependencies (8)Versions (7)Used By (0)

PHPStan Clean Test rules
========================

[](#phpstan-clean-test-rules)

[![Continuous Integration](https://github.com/ikvasnica/phpstan-clean-test/workflows/continuous-integration/badge.svg?event=push)](https://github.com/ikvasnica/phpstan-clean-test/workflows/continuous-integration/badge.svg?event=push)[![Coverage Status](https://camo.githubusercontent.com/c44eb4be0eeef1b9e7c5109907178505d803928afde2f20ad1ab45ed1d106395/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f696b7661736e6963612f7068707374616e2d636c65616e2d746573742f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/ikvasnica/phpstan-clean-test?branch=master)[![Codacy Badge](https://camo.githubusercontent.com/8f943a1236a392516d403e7f62540094dfb5b83005212e5d080f44e09e89fcac/68747470733a2f2f6170692e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f6161343065393131373837303439643362633966393837656331383039663562)](https://www.codacy.com/manual/ikvasnica/phpstan-clean-test?utm_source=github.com&utm_medium=referral&utm_content=ikvasnica/phpstan-clean-test&utm_campaign=Badge_Grade)[![Latest Stable Version](https://camo.githubusercontent.com/48ab9d2714d767f279538e173ba00bb8015ce18a7d2fcb191d67eaa9263c7685/68747470733a2f2f706f7365722e707567782e6f72672f696b7661736e6963612f7068707374616e2d636c65616e2d746573742f76657273696f6e)](https://packagist.org/packages/ikvasnica/phpstan-clean-test)[![License](https://camo.githubusercontent.com/20f2ab224101ade087b090d51bb31f5dfa0f8ead17823f74c8a01dee6ca6b3e8/68747470733a2f2f706f7365722e707567782e6f72672f696b7661736e6963612f7068707374616e2d636c65616e2d746573742f6c6963656e7365)](https://packagist.org/packages/ikvasnica/phpstan-clean-test)

- [PHPStan](https://github.com/phpstan/phpstan)
- [PHPStan-PHPUnit extension](https://github.com/phpstan/phpstan-phpunit)

This extension provides highly opinionated and strict rules for test cases for the PHPStan static analysis tool.

Installation
------------

[](#installation)

Run

```
$ composer require --dev ikvasnica/phpstan-clean-test
```

Usage
-----

[](#usage)

All of the [rules](https://github.com/ikvasnica/phpstan-clean-test#rules) provided by this library are included in [`rules.neon`](rules.neon).

When you are using [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer), `rules.neon` will be automatically included.

Otherwise you need to include `rules.neon` in your `phpstan.neon`:

```
# phpstan.neon
includes:
    - vendor/ikvasnica/phpstan-clean-test/rules.neon
```

Rules
-----

[](#rules)

This package provides the following rules for use with [`phpstan/phpstan`](https://github.com/phpstan/phpstan):

- [`ikvasnica\PHPStan\Rules\UnitExtendsFromTestCaseRule`](#unitextendsfromtestcaserule)
- [`ikvasnica\PHPStan\Rules\DisallowSetupAndConstructorRule`](#disallowsetupandconstructorrule)
- [`ikvasnica\PHPStan\Rules\AssertSameOverAssertEqualsRule`](#assertsameoverassertequalsrule)
- [`ikvasnica\PHPStan\Rules\StaticAssertOverThisAndStaticRule`](#staticassertoverthisandstaticrule)
- [`ikvasnica\PHPStan\Rules\NoNullableArgumentRule`](#nonullableargumentrule)

### `UnitExtendsFromTestCaseRule`

[](#unitextendsfromtestcaserule)

This rule forces you to extend only from allowed classes in unit tests (default: `PHPUnit\Framework\TestCase`).

**Why:**

1. It prevents developers i.e. from using a dependency injection container in unit tests (`$this->getContainer()`) and other tools from integration/functional tests.
2. You should extend only from a class when a child class satisfies the "is a" relationship. That said, if you need only a subset of a parent's functionality, you should use composition over inheritance (i.e. by traits or helpers).

❌

```
// tests/ExampleTestCase/Unit/UnitExtendsInvalidTest.php
namespace ExampleTestCase\Unit;

final class UnitExtendsInvalidTest extends \Dummy\FunctionalDummyTest {}
```

✅

```
// tests/ExampleTestCase/Unit/UnitExtendsUnitTest.php
namespace ExampleTestCase\Unit;

final class UnitExtendsUnitTest extends \PHPUnit\Framework\TestCase {}
```

#### Defaults

[](#defaults)

- By default, this rule detects unit tests by checking the namespace (it must contain the string `Unit`) and the class name ending (it must end with the string `Test`).
- The following class is allowed to be extended: `PHPUnit\Framework\TestCase`

#### Allowing classes to be extended

[](#allowing-classes-to-be-extended)

If you want to allow additional classes to be extended, you can add it to the `classesAllowedToBeExtendedInTests` parameter to a list of class names.

#### Detecting unit tests namespace

[](#detecting-unit-tests-namespace)

If you want to change the namespace string check described above, you can set your own string to be checked in the `unitTestNamespaceContainsString` parameter.

```
# phpstan.neon
parameters:
    ikvasnica:
        classesAllowedToBeExtendedInTests:
          - MyNamespace\AbstractTest
        unitTestNamespaceContainsString: CustomTestPath
```

### `DisallowSetupAndConstructorRule`

[](#disallowsetupandconstructorrule)

Neither of methods `__construct` nor `setUp` can be declared in a unit test.

**Why:**Each test scenario should create its dependencies on its own. Method `setUp` is useful for setting up i.e. database transaction in a functional test. In a unit test, you should put all the preparation into a testing method or a data provider itself. It increases readability and clearly shows the code intention.

#### Detecting unit tests namespace

[](#detecting-unit-tests-namespace-1)

If you want to change the namespace string check described above, you can set your own string to be checked in the `unitTestNamespaceContainsString` parameter.

#### Allowing setUp() method

[](#allowing-setup-method)

If you really want to use the setUp() method, you can whitelist it by setting the parameter `allowSetupInUnitTests` to `true`.

```
# phpstan.neon
parameters:
    ikvasnica:
        unitTestNamespaceContainsString: CustomTestPath
        allowSetupInUnitTests: true
```

❌

```
// tests/ExampleTestCase/Unit/DisallowSetupConstructInvaliTest.php
namespace ExampleTestCase\Unit;

use PHPUnit\Framework\Assert;

final class DisallowSetupConstructInvaliTest extends \PHPUnit\Framework\TestCase
{
    private $something;

    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);
    }

    protected function setUp(): void
    {
        parent::setUp();

        $this->something = true;
    }

    public function testSomeThing(): void
    {
        Assert::assertTrue($this->something);
    }
}
```

✅ ```
// tests/ExampleTestCase/Unit/DisallowSetupConstructOkTest.php
namespace ExampleTestCase\Unit;

use PHPUnit\Framework\Assert;

final class DisallowSetupConstructOkTest extends \PHPUnit\Framework\TestCase
{
    public function testSomeThing(): void
    {
        Assert::assertTrue(true);
    }
}
```

### `AssertSameOverAssertEqualsRule`

[](#assertsameoverassertequalsrule)

Calling `assertEquals` in tests is forbidden in favor of `assertSame`.

**Why:**When using `assertEquals`, data types are not considered. On the other hand, `assertSame` checks whether two variables are of the same type and references the same object. Therefore, `assertEquals` can be valid when comparing objects or arrays, but not scalar values.

Using `assertEquals` with scalar values might lead to an unexpected behaviour (e.g. `assertEquals(null, '')` evaluates to `true`, whereas `assertSame(null, '')` evaluates to `false`).

❌

```
// tests/ExampleTestCase/Unit/InvalidAssertEqualsUses.php
use PHPUnit\Framework\Assert;

$booleanValue = false;
$exception = new Exception('A bad thing has happened.');

Assert::assertEquals(true, $booleanValue);
Assert::assertEquals('exception message', (string) $exception);
```

✅ ```
// tests/ExampleTestCase/Unit/ValidAsserts.php
use PHPUnit\Framework\Assert;

$booleanValue = false;
$exception = new Exception('A bad thing has happened.');
$emptyArray = [];

Assert::assertTrue($booleanValue);
Assert::assertSame('exception message', (string) $exception);

Assert::assertEquals([], $emptyArray);
```

### `StaticAssertOverThisAndStaticRule`

[](#staticassertoverthisandstaticrule)

Calling `$this->assert*`, `self::assert*` or `static::assert*` in tests is forbidden in favor of `PHPUnit\Framework\Assert::assert*`.

**Why:**When you use PHPUnit, your test cases extend from `\PHPUnit\Framework\TestCase`. Assert methods are declared as static there, therefore it does not make sense to call them dynamically. Using `static::assert*` is discouraged, because it is a misuse of inheritance and assertion methods are more like a helper's methods.

❌

```
// tests/ExampleTestCase/Unit/InvalidAssertUsage.php
namespace ExampleTestCase;

final class InvalidAssertUsageTest extends \PHPUnit\Framework\TestCase
{
    public function dummyTest(): void
    {
        // will fail
        $this->assertSame(5, 5);
        $this->assertTrue(false);
        self::assertArrayHasKey(5, [5]);
        static::assertCount(0, []);
        \ExampleTestCase\StaticAssertOverThisAndStaticRule::assertTrue(true);
        InvalidAssertUsageTest::assertTrue(true);
    }
}
```

✅ ```
// tests/ExampleTestCase/Unit/ValidAssertsUsage.php
namespace ExampleTestCase;

use PHPUnit\Framework\Assert;

final class ValidAssertUsageTest extends \PHPUnit\Framework\TestCase
{
    public function dummyTest(): void
    {
        // Assert::anything is OK
        Assert::assertEquals(5, 5);
        Assert::assertCount(1, [1, 2]);
        Assert::assertTrue(false);
        \PHPUnit\Framework\Assert::assertTrue(true);
    }
}
```

### `NoNullableArgumentRule`

[](#nonullableargumentrule)

Nullable arguments and arguments with no type passed to test methods from data providers are forbidden.

**Why:**A nullable argument from a data provider is a code smell. Usually it means that you test two different scenarios in one test. You should divide the test into two scenarios, i.e. one for testing valid data input and one for invalid data when an exception is expected to be thrown.

❌

```
// tests/ExampleTestCase/Unit/NullableArgumentsInTest.php
namespace ExampleTestCase;

final class NullableArgumentsInTest extends \PHPUnit\Framework\TestCase
{
    public function testSomething($mixedTypeArgument, ?string $stringOrNullArgument): void
    {
        // will fail, because the first argument has no type and the second one is nullable
    }

    /**
     * @test
     * @param string|null $maybeString
     */
    public function someTestMethod(?string $maybeString): void
    {
        // will fail too, because this one is a test method due to the annotation
    }
}
```

✅ ```
// tests/ExampleTestCase/Unit/ValidArgumentsInTest.php
namespace ExampleTestCase;

final class ValidArgumentsInTest extends \PHPUnit\Framework\TestCase
{
    public function testSomething(string $definitelyString): void
    {
        // this is a way to go!
    }

    public function testSomethingElse(): void
    {
        // no arguments are, of course, allowed
    }

    public function anotherPublicMethod(?bool $maybeTrueOrFalse): void
    {
        // although weird, you may have other non-test public methods with a nullable type as well
    }

    private function testHelperMethod(?string $maybeString): void
    {
        // private and protected methods are allowed to have nullable arguments
    }
}
```

###  Health Score

32

—

LowBetter than 71% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity27

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~261 days

Recently: every ~326 days

Total

6

Last Release

989d ago

PHP version history (4 changes)0.1PHP ~7.1

0.2PHP &gt;=7.1

0.3PHP &gt;=7.2

0.4PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/0f41bf6c2ae32efee7fb3c45b0589644d109b99da4f3aa088eaed995c4ab3f72?d=identicon)[ikvasnica](/maintainers/ikvasnica)

---

Top Contributors

[![ikvasnica](https://avatars.githubusercontent.com/u/4759802?v=4)](https://github.com/ikvasnica "ikvasnica (25 commits)")

---

Tags

PHPStantestsphpstan-rulesphpstan-strict-rules

###  Code Quality

TestsPHPUnit

Code StyleECS

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ikvasnica-phpstan-clean-test/health.svg)

```
[![Health](https://phpackages.com/badges/ikvasnica-phpstan-clean-test/health.svg)](https://phpackages.com/packages/ikvasnica-phpstan-clean-test)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k43.5M5.2k](/packages/larastan-larastan)[ekino/phpstan-banned-code

Detected banned code using PHPStan

2925.6M92](/packages/ekino-phpstan-banned-code)[shipmonk/dead-code-detector

Dead code detector to find unused PHP code via PHPStan extension. Can automatically remove dead PHP code. Supports libraries like Symfony, Doctrine, PHPUnit etc. Detects dead cycles. Can detect dead code that is tested.

3462.2M51](/packages/shipmonk-dead-code-detector)[szepeviktor/phpstan-wordpress

WordPress extensions for PHPStan

3257.8M887](/packages/szepeviktor-phpstan-wordpress)[sidz/phpstan-rules

Set of PHPStan rules

31408.9k11](/packages/sidz-phpstan-rules)[shipmonk/phpstan-baseline-per-identifier

Split your PHPStan baseline into multiple files, one per error identifier. Supports both neon baseline and PHP baseline.

931.1M26](/packages/shipmonk-phpstan-baseline-per-identifier)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
