Laravel: Using mock instance in Customised Route::

2019-08-02 21:55发布

问题:

Route model binding in Laravel, helps bind an instance to the service container, to be used in a controller action. And the beautiful thing is that when I want to write tests, I can bind a mock instance to the application, which replaces what is bound to the container. - App::instance($className, $mockInstance);

I have made a request to the route (Route::get("/{class_name}")). I want to return an instance of the class, depending on what the value of the route parameter class_name is.

If the value is two, I want an instance of the Two class, likewise for the One class. Notice that both classes inherit from AbstractClass, so the returned instance is of type AbstractClass, (PHP7).

Problem: When I make visit the route, I get the correct instance. But when I want to use the mock instance, it overrides the mock instance and creates/returns the main instance. I need to use the mock instance so I can set expectations on it.

I've added some code, which I hope illustrates what I mean.

abstract class AbstractClass {
// abstract parent class
}

class One extends AbstractClass {
// single child class
}

class Two extends AbstractClass {
// another single child class
}

// I put this inside the `bind` method of the RouteServiceProvider
Route::bind("class_name", function (string $class_name) : AbstractClass {
 // I wish to return an instance to the route, based on the string that was passed - "one" or "two"
 $class = ucfirst($class_name);
 return new $class();
});

// test
public function testSomething () {
 $mockInstance = Mockery::mock(AbstractClass::class);
 App::instance(AbstractClass::class, $mockInstance);
 $response = $this->get("/one");
}
// controller method
public function action (AbstractClass $instance) {
 // I want to see/use the mock instance here, for testing
 // and the main instance otherwise.
}

I am using PHP7.0, with Laravel5.4 for development

回答1:

Reason: Well I guess you're confusing Router bindings vs Container Bindings. These two are completely separate in nature.

Answer: Just update the router bindings when you're writing tests.

        $mock = Mockery::mock(AbstractClass::class);

        $this->app->get('router')->bind('class', function () use ($mock) {
             return $mock;
        });

        $response = $this->get("/test");

Router Bindings: These bindings are only resolved when routing is involved, and it cannot be resolved when you're using app() instance.

Container Bindings: Container bindings are separate and it doesn't know about the router bindings at all.