leomarquine's avatar

How can I test a custom package that use Facades?

If I have this example class in my package:

<?php

namespace Foo\Bar;

use Illuminate\Support\Facades\DB;

class Example
{
    protected $model;

    public function __construct($model)
    {
        $this->model = $model;
    }

    public function execute()
    {
        $dummyData = collect([
            ['foo' => 'bar'],
            ['foo' => 'baz'],
        ]);

        foreach ($dummyData->chunk(100) as $chunk) {
            DB::transaction(function () use ($chunk) {
                $this->model->insert($chunk->toArray());
            });
        }
    }
}

And this TestExemple.php

<?php

use Foo\Bar\Example;

class TestExemple extends TestCase
{
    /** @test */
    function it_should_load_data()
    {
        $model = $this->getModel(); // TestCase class

        $example = new Example($model);
        
        $example->execute();

        $this->assertEquals(2, $model->all()->count());
    }
}

When i run phpunit i get:

1) TestExemple::it_should_load_data
RuntimeException: A facade root has not been set.

/path/to/package/vendor/illuminate/support/Facades/Facade.php:210
/path/to/package/src/Example.php:24

How can I make the Facade work without a full Laravel installation?

0 likes
14 replies
dev@menudrive.com's avatar

Have you set up the provider and alias in config/app.php? Judging from the facade root has not been set message that is where I would start looking.

leomarquine's avatar

I don't have the config/app.php file, its a custom package and I'm trying to write tests using only the dependencies I need without a full Laravel installation.

dev@menudrive.com's avatar

I'm unsure. But it does appear that the piece that is failing is:

DB::transaction(function () use ($chunk) {
                $this->model->insert($chunk->toArray());

This is probably because Illuminate\Support\Facades\DB returns the string 'db'. So you'll have to connect that string to registering an instance of the database class through the Illuminate\Database\DatabaseServiceProvider. I think you're going to have to make your own equivalent of a config/app.php along with a way to call it, like how Laravel does when it boots.

Alternatively, the github issue linked to by the question talks about creating a new app container to use the facade with the context of using standalone Eloquent. This seems like it's what you want. Have you tried that?

leomarquine's avatar

Just tried it, getting a different error now:

1) ExampleTest::it_should_load_data
ReflectionException: Class db does not exist

path/to/package/vendor/illuminate/container/Container.php:738
path/to/package/vendor/illuminate/container/Container.php:633
path/to/package/vendor/illuminate/container/Container.php:1178
path/to/package/vendor/illuminate/support/Facades/Facade.php:151
path/to/package/vendor/illuminate/support/Facades/Facade.php:120
path/to/package/vendor/illuminate/support/Facades/Facade.php:207
path/to/package/src/Example.php:26
path/to/package/tests/ExampleTest.php:14
leomarquine's avatar

Hey @dev@menudrive.com, I was in a rush, saw a different error and thought it was an assertion error. But actually, after i tried:

$app = new Container();
$app->singleton('app', 'Illuminate\Container\Container');

Facade::setFacadeApplication($app);
$app->bind('db', 'Illuminate\Database\DatabaseManager');

I'm getting this error:

1) ExampleTest::it_should_load_data
Illuminate\Contracts\Container\BindingResolutionException: Unresolvable dependency resolving [Parameter #0 [ <required> $app ]] in class Illuminate\Database\DatabaseManager
leomarquine's avatar

Working code on the setUp method in my test:

$app = new Container();
$app->singleton('app', 'Illuminate\Container\Container');

$app->singleton('config', 'Illuminate\Config\Repository');

$app['config']->set('database.default', 'test');
$app['config']->set('database.connections.test', [
    'driver'   => 'sqlite',
    'database' => ':memory:',
]);

$app->bind('db', function ($app) {
    return new DatabaseManager($app, new ConnectionFactory($app));
});
1 like
arcadev's avatar

Was looking up this issue myself and thought I'd share my experience. I was about to add the Container and bind as in the example above when I realized that the test that I'm working on is calling DB from __construct(). Before trying the code above, I switched the method from __construct() to setUp(), calling parent::setUp() as the first line. Executed fine. No more root error.

2 likes
stormz's avatar

Hello.

I have the same problem with Config.

I can't find what to put in setUp in order to not have the "A facade root has not been set." error.

Do you solve this ?

Thank you.

phpguru's avatar

Config works, logging (via Log::debug( ) ) doesn't. Hmm... running into the same sort of issue with a custom package that I want to unit test certain log writing features of the package.

Please or to participate in this conversation.