laravel / scout

Laravel Scout provides a driver based solution to searching your Eloquent models.
https://laravel.com/docs/scout
MIT License
1.55k stars 329 forks source link

Models using Searchable not usable in unit test #641

Closed joostdebruijn closed 2 years ago

joostdebruijn commented 2 years ago

Description:

When writing unit tests, using models with the Searchable treat are failing during booting of the model with the message A facade root has not been set.. The second time the model is used, the error doesn't occur again (probably because the model is booted earlier). Disabling the Searchable treat on the model or using CreatesApplication for the tests solves the problem. However, I think it should be possible to use searchable models without booting the application.

During the tests the env SCOUT_DRIVER is set to "collection"

Steps To Reproduce:

  1. Create a model with a factory and use the Searchable treat for the model.
  2. Use the model in a unit test:
use App\Models\SearchableModel;

// First test
it('should do something', function () {
    $seachable_model = SearchableModel::factory()->make(); // Fails: "A facade root has not been set."

    $this->assertEqual('foo', $searchable_model->bar);
});

// Second test
it('should do something', function () {
    $seachable_model = SearchableModel::factory()->make(); // Not failing

    $this->assertEqual('foo', $searchable_model->bar);
});
driesvints commented 2 years ago

Hi there,

Thanks for reporting but it looks like this is a question which can be asked on a support channel. Please only use this issue tracker for reporting bugs with the library itself. If you have a question on how to use functionality provided by this repo you can try one of the following channels:

However, this issue will not be locked and everyone is still free to discuss solutions to your problem!

Thanks.

edouardgab commented 1 year ago

Hi @joostdebruijn ! Any chance you were able to resolve that issue? We have the exact same situation.

Thanks!

crynobone commented 1 year ago

However, I think it should be possible to use searchable models without booting the application.

The answer is no, and this is not a bug.

edouardgab commented 1 year ago

I agree, I do use CreatesApplication trait in TestCase.php, but even with this, I still get the error.

crynobone commented 1 year ago

Hey there, thanks for reporting this issue.

We'll need more info and/or code to debug this further. Can you please create a repository with the command below, commit the code that reproduces the issue as one separate commit on the main/master branch and share the repository here?

Please make sure that you have the latest version of the Laravel installer in order to run this command. Please also make sure you have both Git & the GitHub CLI tool properly set up.

laravel new bug-report --github="--public"

Do not amend and create a separate commit with your custom changes. After you've posted the repository, we'll try to reproduce the issue.

Thanks!

joostdebruijn commented 1 year ago

@edouardgab I solved it by setting the SCOUT_DRIVER env to collection in my testing environment. That enables the testing of Scout-enabled models and features, without dealing with complex search drivers.

In phpunit.xml:

<env name="SCOUT_DRIVER" value="collection"/>
edouardgab commented 1 year ago

Thanks @joostdebruijn , unfortunately that doesn't seems to help in my case. The setUp in TestCase (which boot the application) is called after the Searchable trait, and static::observe(new ModelObserver); from bootSearchable needs Facade booted. I was able to make it work by manually triggering createApplication() in the construct of my test class, but that doesn't seem clean + it looks like I should not have to do this.

Still looking into it ... Thank you for your answer on this fairly old issue.

crynobone commented 1 year ago

If you are manually triggering CreatesApplication then you're not extending the correct TestCase, Model should only be tested using Feature Tests and not Unit Tests.

edouardgab commented 1 year ago

From that I see, I am extending the right TestCase. I am doing Feature Tests.

The TestCase.php looks like this :

namespace Tests;

use App\Models\User;
use App\Classes\PermissionHelper;
use App\Services\LocationService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

   ....
}

And my test class :

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Company;
use App\Models\Customer;
use App\Services\CustomerService;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class CustomerTest extends TestCase {
}

I don't have any issue when Searchable Trait is not enable.

The backtrace looks like this :

An error occurred inside PHPUnit.

Message:  A facade root has not been set.
Location: /app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:350

#0 /app/vendor/laravel/scout/src/ModelObserver.php(46): Illuminate\Support\Facades\Facade::__callStatic('get', Array)
#1 /app/vendor/laravel/scout/src/Searchable.php(27): Laravel\Scout\ModelObserver->__construct()
#2 [internal function]: App\Models\Customer::bootSearchable()
#3 /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(301): forward_static_call(Array)
#4 /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(281): Illuminate\Database\Eloquent\Model::bootTraits()
#5 /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(257): Illuminate\Database\Eloquent\Model::boot()
#6 /app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(235): Illuminate\Database\Eloquent\Model->bootIfNotBooted()
#7 /app/app/Services/CustomerService.php(23): Illuminate\Database\Eloquent\Model->__construct()
#8 /app/tests/Feature/CustomerTest.php(25): App\Services\CustomerService->__construct()
#9 /app/vendor/phpunit/phpunit/src/Framework/TestBuilder.php(55): Tests\Feature\CustomerTest->__construct('testCustomerCre...')
#10 /app/vendor/phpunit/phpunit/src/Framework/TestSuite.php(477): PHPUnit\Framework\TestBuilder->build(Object(ReflectionClass), 'testCustomerCre...')
#11 /app/vendor/phpunit/phpunit/src/Framework/TestSuite.php(125): PHPUnit\Framework\TestSuite->addTestMethod(Object(ReflectionClass), Object(ReflectionMethod))
#12 /app/vendor/phpunit/phpunit/src/Framework/TestSuite.php(215): PHPUnit\Framework\TestSuite::fromClassReflector(Object(ReflectionClass))
#13 /app/vendor/phpunit/phpunit/src/Framework/TestSuite.php(244): PHPUnit\Framework\TestSuite->addTestSuite(Object(ReflectionClass))
#14 /app/vendor/phpunit/phpunit/src/Framework/TestSuite.php(261): PHPUnit\Framework\TestSuite->addTestFile('/app/tests/Feat...')
#15 /app/vendor/phpunit/phpunit/src/TextUI/Configuration/Xml/TestSuiteMapper.php(77): PHPUnit\Framework\TestSuite->addTestFiles(Array)
#16 /app/vendor/phpunit/phpunit/src/TextUI/Configuration/TestSuiteBuilder.php(62): PHPUnit\TextUI\XmlConfiguration\TestSuiteMapper->map('/app/phpunit.xm...', Object(PHPUnit\TextUI\Configuration\TestSuiteCollection), '', '')
#17 /app/vendor/phpunit/phpunit/src/TextUI/Application.php(356): PHPUnit\TextUI\Configuration\TestSuiteBuilder->build(Object(PHPUnit\TextUI\Configuration\Configuration))
#18 /app/vendor/phpunit/phpunit/src/TextUI/Application.php(103): PHPUnit\TextUI\Application->buildTestSuite(Object(PHPUnit\TextUI\Configuration\Configuration))
#19 /app/vendor/phpunit/phpunit/phpunit(99): PHPUnit\TextUI\Application->run(Array)
#20 {main}

The setUp from TestCase.php is not called yet. But this is where the application is started (in Illuminate\Foundation\Testing\TestCase.php)

crynobone commented 1 year ago

Need to see full reproduction repository or you shiuld ask this question in the suggested support channel as requested and suggested earlier. Goodbye