Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using DatabaseMigrations trait prevents overriding console command dependencies #15455

Closed
davidhemphill opened this issue Sep 16, 2016 · 4 comments

Comments

@davidhemphill
Copy link
Contributor

davidhemphill commented Sep 16, 2016

  • Laravel Version: 5.3.9
  • PHP Version: 7.0.7
  • Database Driver & Version: SQLite/MySQL

Description:

It seems as though you can't swap out the IoC bindings for console commands when you're using the DatabaseMigrations trait in your tests.

For example, I've set up an default binding for and interface in AppServiceProvider . I also, have a console command which typehints the interface. During my test, I'm trying to swap out that dependency for a dummy one, but using $this->artisan('command') still uses the default binding. This is because I'm using DatabaseMigrations. Removing it allows me to swap out the binding like expected.

Steps To Reproduce:

Here's a example that demonstrates this behavior. You can also run the tests for this example repository: https://github.com/davidhemphill/console-bindings

In AppServiceProvider I've set up a binding like this.

$this->app->bind(GatewayInterface::class, ActualGateway::class);

And this is a test for a console command that uses GatewayInterface as a dependency.

<?php

use App\DummyGateway;
use App\GatewayInterface;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class YellTest extends TestCase
{
    use DatabaseMigrations;

    function testExample()
    {
        // Swap out the binding...or so we think. Instead of the default
        // ActualGateway implementation we want to use DummyGateway
        $this->app->bind(GatewayInterface::class, DummyGateway::class);

        // Outputs App/DummyGateway proving our binding is correct
        // dd(app(GatewayInterface::class));

        // Console command uses GatewayInterface as a dependency
        // what is actually resolved is an instance of ActualGateway
        $this->artisan('gateway-stuff');

        // This will work however
       // $this->app[App\Console\Kernel::class]->call('gateway-stuff');
    }
}
@JacobBennett
Copy link
Contributor

I believe this is because $this->artisan('migrate') is being called by the DatabaseMigrations trait before your test method runs. This causes the application to resolve the Console\Kernel class before your test method. It seems then that when you call $this->artisan() in your test, that it is looking to the already resolved instance of $this->app rather than re-resolving the Kernel from the container... When you specifically ask the container to resolve the Kernel again however, it takes the new bindings into affect?

This is a good guess as to what is happening, but I don't have a solution by any means, just what I learned from a similar nightmare of an experience yesterday chasing a similar problem down.

@themsaid
Copy link
Member

When you use the DatabaseMigrations trait in tests, a call to $this->artisan('migrate') is made for every test, this call creates an instance of Illuminate\Console\Application and resolves all commands.

Inside any test if you try to swap any console command dependency using $this->app->bind() and then call the command via $this->artisan('command') the old concrete implementation will be called instead of the swapped dummy one.

The problem is that we build Illuminate\Console\Application too early, it resolves the commands using the original bindings and no way of re-resolving. It's a bit tricky to fix.

@JacobBennett
Copy link
Contributor

Thanks @themsaid and @taylorotwell. Nice work! That was LIGHTNING fast!

@davidhemphill
Copy link
Contributor Author

Thanks you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants