I use PHPUnit for my unit tests, but I don't rely on mocking so I don't want to include mockery/mockery in the require-dev section of my composer.json file. Unfortunately, when removing this dependency, all tests involving the database fail. For example the following stupid test
class ExampleTest extends TestCase {
use RefreshDatabase;
public function test_empty_users(): void {
$this->assertDatabaseEmpty('users');
}
}
fails with error stack
1) Tests\Feature\ExampleTest::test_empty_users
Error: Class "Mockery" not found
example-app\vendor\...\src\Illuminate\Testing\PendingCommand.php:528
example-app\vendor\...\src\Illuminate\Testing\PendingCommand.php:447
example-app\vendor\...\src\Illuminate\Testing\PendingCommand.php:653
example-app\vendor\...\src\Illuminate\Foundation\Testing\Concerns\InteractsWithConsole.php:87
example-app\vendor\...\src\Illuminate\Foundation\Testing\RefreshDatabase.php:101
example-app\vendor\...\src\Illuminate\Foundation\Testing\RefreshDatabase.php:84
example-app\vendor\...\src\Illuminate\Foundation\Testing\RefreshDatabase.php:25
example-app\vendor\...\src\Illuminate\Foundation\Testing\Concerns\InteractsWithTestCaseLifecycle.php:207
example-app\vendor\...\src\Illuminate\Foundation\Testing\Concerns\InteractsWithTestCaseLifecycle.php:96
example-app\vendor\...\src\Illuminate\Foundation\Testing\TestCase.php:44
If Laravel wants to use Mockery internally, it sure can. But why do I need to add this as a dependency of my project too?
Well, from a software engineering point of view, I am bothered by the idea that I should be listing in my own composer.json file libraries on which I only indirectly depend. Like isn't this the whole purpose of tools such as Composer to automatically import transitive dependencies?
From reading online, I understand that this has to do with the fact that indirect require-dev dependencies are not automatically loaded. But then shouldn't the official Laravel documentation mention that importing Mockery is a requirement to use Laravel's test helpers?
EDIT: Thinking about it, maybe the clean solution would be that Laravel test helpers like the RefreshDatabase trait would be in a separate PHP library, let us call it laravel/tests, which listed Mockery as a require dependency. Then I could import in my Composer configuration laravel/tests, which makes sense since I am indeed using this trait, and the indirect dependency would automatically follow.
It's interesting because laravel/framework has mockery/mockery as dev dependency too, so it should be available even if deleted from laravel/laravel project, but it is not. Does anybody know why?
this has to do with the fact that indirect require-dev dependencies are not automatically loaded
Dependency can either be required for the package to work properly (then it should be in require section and it is always installed), or required only for development of this package (then it should be in require-dev section and is installed only when package repository is root)
So, dev dependencies are not "inherited". I didn't know it...
Thank you for your link. Funnily enough, the accepted answer suggests exactly what I was proposing: that Laravel should put its testing utilities in a dedicated laravel/testing library which imports Mockery as a require dependency, so that when I register laravel/testing in my require-dev section, it does import Mockery correctly.
@Glukinho I see it as a matter of dependency hygiene. The current solution (besides being undocumented), breaks dependency encapsulation. Your own project shouldn't have to know about the internal implementation details (like the use of Mockery) of its own dependencies. What I am using is the RefreshDatabase trait from the \src\Illuminate\Foundation\Testing\ package. So it makes perfect sense that I should be importing a library such as laravel/testing (since I am directly using its classes). The fact that, under the hood, the developers of this class want to rely on Mockery shouldn't be my concern. In fact, I don't even know what version of Mockery their code needs. If you had to list every cascade dependency of each package you use, it would be a nightmare. I had the feeling that such considerations where at the core of dependency management, so I am quite surprised by the choice made by the Laravel developers in this context.