jimmy.puckett's avatar

Need way to test a service provider to when it has a call to the global "config" function

So we are building a package for laravel, and would like to have test on our Service Provider to make sure that we are interacting with the Laravel Service Provider as expected.

We are registering configs with the app, and just want to make sure that we are calling the correct methods. In the provider, we are using the global "config" function to get the path to the config files, which cannot be easly mocked out.

Here is what the service provider looks like...

<?php

namespace Package\GreatThing;

use Illuminate\Support\ServiceProvider;

/**
 * Class GreatThingServiceProvider
 *
 * @package Package\GreatThing
 */
class GreatThingServiceProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        $config_file = realpath(__DIR__ . '/config/greatthing.php');

        $this->publishes([
            $config_file => config_path('greatthing.php'),
        ]);

        $this->mergeConfigFrom($config_file, 'greatthing');
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

Here is what the test looks like...

<?php

namespace Tests\Package\GreatThing;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Mockery;
use Package\GreatThing\GreatThingServiceProvider;

class GreatThingServiceProviderTest extends TestCase
{
    /**
     * @var Mockery\Mock
     */
    protected $application_mock;

    /**
     * @var ServiceProvider
     */
    protected $service_provider;

    protected function setUp()
    {
        $this->setUpMocks();

        $this->service_provider = new GreatThingServiceProvider($this->application_mock);

        parent::setUp();
    }

    protected function setUpMocks()
    {
        $this->application_mock = Mockery::mock(Application::class);
    }

    /**
     * @test
     */
    public function it_can_be_constructed()
    {
        $this->assertInstanceOf(ServiceProvider::class, $this->service_provider);
    }

    /**
     * @test
     */
    public function it_does_nothing_in_the_register_method()
    {
        $this->assertNull($this->service_provider->register());
    }

    /**
     * @test
     */
    public function it_performs_a_boot_method()
    {
        $this->application_mock->shouldReceive('publishes')
                               ->once()
                               ->with([
                                   '/some/path/greatthing.php',
                               ])
                               ->andReturnNull();

        $this->application_mock->shouldReceive('mergeConfigFrom')
                               ->once()
                               ->withArgs([
                                   '/config/greatthing.php',
                                   'greatthing',
                               ])
                               ->andReturnNull();

        $this->service_provider->boot();
    }
}

Does anyone have any good ways to get to the config path?

0 likes
6 replies
neomerx's avatar
neomerx
Best Answer
Level 1

You can rewrite your provider a bit...

as config_path is actually

    function config_path($path = '')
    {
        return app()->make('path.config').($path ? DIRECTORY_SEPARATOR.$path : $path);
    }

You can replace config_path('greatthing.php') it with something like

$this->app->make('path.config') . '/greatthing.php';

where $this->app will be your mock

UPD Actual solution you can see here and related tests here.

jimmy.puckett's avatar

@neomerx thanks for taking the time to respond. I forgot to mention that I had tested that & get this error...

PHP Fatal error:  Cannot use object of type Mockery_0_Illuminate_Contracts_Foundation_Application as array in /Users/user/Documents/GIT/great-thing/vendor/illuminate/support/ServiceProvider.php on line 64

Here is the new method...

    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        $config_file = realpath(__DIR__ . '/config/greatthing.php');

        $config_path = $this->app->make('path.config') . DIRECTORY_SEPARATOR . 'greatthing.php';

        $this->publishes([
            $config_file => $config_path,
        ]);

        $this->mergeConfigFrom($config_file, 'greatthing');
    }

and here is the test...

    /**
     * @test
     */
    public function it_performs_a_boot_method()
    {
        $this->application_mock->shouldReceive('make')
                               ->with('path.config')
                               ->once()
                               ->andReturn('/some/path');

        $this->application_mock->shouldReceive('publishes')
                               ->once()
                               ->with([
                                   '/some/path/greatthing.php',
                               ])
                               ->andReturnNull();

        $this->application_mock->shouldReceive('mergeConfigFrom')
                               ->once()
                               ->withArgs([
                                   '/config/greatthing.php',
                                   'greatthing',
                               ])
                               ->andReturnNull();

        $this->service_provider->boot();
    }

Am I mocking the make wrong?

jimmy.puckett's avatar

@neomerx thanks so much for doing this test. I'm actually at laracon, so I won't get a chance to test until tonight or tomorrow.

I'll post back what happens!

jimmy.puckett's avatar

@neomerx That was it. I am now getting the test to run over the Service Provider. Thanks.

Now I just want/need to make sure that that I get a few assertions on the test to make 100% sure that I am doing exactly what I want/need in the boot.

Thanks again!

koufax's avatar

@neomerx

This is really good stuff. Thank you for spreading the knowledge. :)

Please or to participate in this conversation.