composer require --dev lendable/phpunit-extensions
use Lendable\PHPUnitExtensions\TestCase;
class MyTest extends TestCase
{
public function testExample()
{
$mock = $this->createStrictMock(MyClass::class);
// All methods must be explicitly stubbed; no defaults.
}
}
use Lendable\PHPUnitExtensions\StrictMocking;
class MyTest extends \PHPUnit\Framework\TestCase
{
use StrictMocking;
public function testExample()
{
$mock = $this->createStrictMock(MyClass::class);
// Strict mocking enforced.
}
}
Debugging a flaky test: Replace loose mocking:
$mock = $this->createMock(MyClass::class); // Returns defaults (e.g., `0` for `(): int`).
With strict mocking:
$mock = $this->createStrictMock(MyClass::class); // Fails if any method is called unstubbed.
This catches unintended assumptions early.
TestCase or per-class via StrictMocking trait.$mock = $this->createStrictMock(MyService::class);
$mock->expects($this->once())
->method('fetchData')
->willReturn(['test']);
BadMethodCallException (vs. returning defaults).$mock = $this->createStrictMock(MyService::class);
$this->app->instance(MyService::class, $mock);
LendableTestCase alongside Laravel’s TestCase (if using traits):
use Lendable\PHPUnitExtensions\StrictMocking;
class FeatureTest extends \Tests\TestCase
{
use StrictMocking;
// Strict mocking applies to all test methods.
}
phpstan.neon:
includes:
- vendor/lendable/phpunit-extensions/phpstan/rules.neon
lendable_phpunit:
enforceStrictMocking:
pardoned:
- Tests\Legacy\OldTest
vendor/bin/phpstan analyse
Use createStrictPartialMock for classes with __construct:
$mock = $this->createStrictPartialMock(MyClass::class, ['arg1']);
$mock->method('unstubbedMethod')->willThrowException(new \RuntimeException());
Existing Tests Break:
createMock()) return defaults (e.g., 0 for (): int). Strict mocks fail if unstubbed.pardoned list.Trait Composition Conflicts:
RefreshDatabase trait, ensure it doesn’t override createMock:
// Avoid this in strict tests:
$mock = $this->getMockBuilder(MyClass::class)->getMock();
createStrictMock()/createStrictPartialMock().PHPStan False Positives:
$mock->{$method}()) may trigger false positives.pardoned or suppress rules for specific classes.Performance Overhead:
vendor/bin/phpunit --coverage-text
$this->createStrictMock(MyClass::class, [], '', true); // 4th arg: enable exceptions.
vendor/bin/phpstan analyse --level=max --error-format=github
$mock = $this->createMock(MyClass::class); // Bypass strict mode.
Custom Strict Mock Builder:
Extend Lendable\PHPUnitExtensions\StrictMockBuilder for project-specific defaults:
class ProjectMockBuilder extends StrictMockBuilder
{
protected function getDefaultMethods(): array
{
return ['__toString' => $this->returnValue('mock')];
}
}
Rector Rules:
Use the bundled EnforceDisableReturnValueGenerationForTestDoublesRector to auto-add #[DisableReturnValueGenerationForTestDoubles] to test classes:
vendor/bin/rector process src/tests --dry-run
Laravel Service Providers:
Override createApplication() to enforce strict mocks in HTTP tests:
protected function createApplication()
{
$app = require __DIR__.'/../../bootstrap/app.php';
$app->make(\Lendable\PHPUnitExtensions\StrictMocking::class);
return $app;
}
composer require --dev phpunit/phpunit:^12
Mockery:
For complex mocks, use Mockery alongside strict PHPUnit mocks:
$mock = Mockery::mock(MyClass::class)->makeStrict();
$mock = $this->createStrictMock(User::factory()->make()->toArray());
# .github/workflows/tests.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: vendor/bin/phpstan analyse --level=max
How can I help you explore Laravel packages today?