Reputation: 742
I'm running into an issue when writing unit tests with PHPUnit using @dataProvider
in a Laravel app. The error I'm receiving is:
PHP Fatal error: Class 'Eloquent' not found in /path/to/project/app/models/ExampleClass.php on line 7
It looks like the constant used in the dataProvider
is causing the fatal.
composer.json:
"psr-4": {
"Acme\\Models\\": "app/models"
}
phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="bootstrap/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Application Test Suite">
<directory>./app/tests/</directory>
</testsuite>
</testsuites>
</phpunit>
The example model:
<?php
namespace Acme\Models;
use Eloquent;
class ExampleClass extends Eloquent
{
/**
* @var bool
*/
const TRUE = true;
}
The example test class:
<?php
use Acme\Models\ExampleClass;
class ExampleClassTest extends TestCase
{
/**
* Example test.
*
* @param int $value
* @return void
* @dataProvider testExampleTestDataProvider
*/
public function testExampleTest($value)
{
$this->assertTrue($value);
}
/**
* Data provider for testExampleTest.
*
* @return array
*/
public function testExampleTestDataProvider()
{
return array(
array(ExampleClass::TRUE),
);
}
}
Stack trace:
PHP Stack trace:
PHP 1. {main}() /usr/local/bin/phpunit:0
PHP 2. PHPUnit_TextUI_Command::main() /usr/local/bin/phpunit:612
PHP 3. PHPUnit_TextUI_Command->run() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:138
PHP 4. PHPUnit_TextUI_Command->handleArguments() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:148
PHP 5. PHPUnit_Util_Configuration->getTestSuiteConfiguration() phar:///usr/local/bin/phpunit/phpunit/TextUI/Command.php:696
PHP 6. PHPUnit_Util_Configuration->getTestSuite() phar:///usr/local/bin/phpunit/phpunit/Util/Configuration.php:837
PHP 7. PHPUnit_Framework_TestSuite->addTestFiles() phar:///usr/local/bin/phpunit/phpunit/Util/Configuration.php:924
PHP 8. PHPUnit_Framework_TestSuite->addTestFile() /path/to/project/vendor/phpunit/phpunit/src/Framework/TestSuite.php:400
PHP 9. PHPUnit_Framework_TestSuite->addTestSuite() /path/to/project/vendor/phpunit/phpunit/src/Framework/TestSuite.php:374
PHP 10. PHPUnit_Framework_TestSuite->__construct() /path/to/project/vendor/phpunit/phpunit/src/Framework/TestSuite.php:289
PHP 11. PHPUnit_Framework_TestSuite->addTestMethod() /path/to/project/vendor/phpunit/phpunit/src/Framework/TestSuite.php:188
PHP 12. PHPUnit_Framework_TestSuite::createTest() /path/to/project/vendor/phpunit/phpunit/src/Framework/TestSuite.php:842
PHP 13. PHPUnit_Util_Test::getProvidedData() /path/to/project/vendor/phpunit/phpunit/src/Framework/TestSuite.php:465
PHP 14. ReflectionMethod->invoke() /path/to/project/vendor/phpunit/phpunit/src/Util/Test.php:392
PHP 15. ExampleClassTest->testExampleTestDataProvider() /path/to/project/vendor/phpunit/phpunit/src/Util/Test.php:392
PHP 16. spl_autoload_call() /path/to/project/vendor/phpunit/phpunit/src/Util/Test.php:27
PHP 17. Composer\Autoload\ClassLoader->loadClass() /path/to/project/vendor/phpunit/phpunit/src/Util/Test.php:0
PHP 18. Composer\Autoload\includeFile() /path/to/project/vendor/composer/ClassLoader.php:278
PHP 19. include() /path/to/project/vendor/composer/ClassLoader.php:386
Upvotes: 7
Views: 2879
Reputation: 17762
Found a neat trick that works for me - you can return a closure in the data provider array, which you execute in the test.
The code in the closure will be executed in the test method, not the data provider function.
#[DataProvider('provideMyData')]
public function testSomething(Closure $getUser)
{
$user = $getUser();
self::assertInstanceOf(UserModel::class, $user);
}
public function provideMyData(): array
{
return [
[
fn() => UserModel::create(['attributes']), // Arrow style syntax
],
[
function () { // Full function style
$user = new UserModel();
$user->name = 'Full Name';
$user->save();
return $user;
},
],
];
}
Upvotes: 0
Reputation: 5663
I was getting the same error, and it was because in my
protected function setUp() : void {
}
in the tests/TestCase.php
I did not call parent::setUp()
when i did, the error went away.
Upvotes: -1
Reputation: 720
You have to run this in your dataProvider. As Thomas Kelley says this because it gets the dataProvider data before it have run CreateApplication code ether if you using that trait or you create application your self in the setUp method. This can also generate The data provider specified for x is invalid.
PHPStorm will only say No tests executed!
if a single test is run.
public function fooDataProvider() {
$this->refreshApplication();
//Code using laravel
}
Upvotes: 1
Reputation: 10269
You need to include the autoloader first.
Something like:
require __DIR__.'/../vendor/autoload.php';
PHPUnit doesn't load your autoloader for you.
Upvotes: -3
Reputation: 10292
I'm having this exact issue. It has to do with the fact that the Laravel aliases (e.g. Config::
, Log::
...) have not been loaded at the time that the @dataProvider
method is called. There are two solutions here that I can think of here.
Solution 1
Modify your @dataProvider
so that it doesn't use the model class. In my case, I was creating model objects in the @dataProvider method, like this:
public function people() {
$person1 = new Person();
$person1->name = "me";
$person2 = new Person();
$person2->name = "you";
return [$person1, $person2];
}
Since the Person
class is referenced in the @dataProvider
method, it's going to attempt to load that class. Then it will fail because the Eloquent
class alias hasn't been created by Laravel yet.
To get around this, I could just return the data, and create the actual model objects in the test itself:
public function people() {
return ["me", "you"];
}
public function testPerson($name) {
$person = new Person();
$person->name = $name;
// Assertions...
}
In your case, that would mean returning [['true']]
, instead of [[ExampleClass::TRUE]]
.
Solution 2
I see no compelling reason to use the Eloquent
class alias here. In fact, I don't know why it exists at all (except perhaps that it "looks" better?). I brought this up in the IRC channel, and didn't get a response... So if there's a reason to use the alias here, then I don't know it.
That said, if your model class extends the underlying \Illuminate\Database\Eloquent\Model
class instead of the Eloquent
alias, then your tests will start working as-is.
<?php
namespace Acme\Models;
use \Illuminate\Database\Eloquent\Model;
class ExampleClass extends Model
{
/**
* @var bool
*/
const TRUE = true;
}
Upvotes: 7