Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Laravel, how to give another implementation to the service container when testing?

I'm creating a Laravel controller where a Random string generator interface gets injected to one of the methods. Then in AppServiceProvider I'm registering an implementation. This works fine.

The controller uses the random string as input to save data to the database. Since it's random, I can't test it (using MakesHttpRequests) like so:

$this->post('/api/v1/do_things', ['email' => $this->email])
->seeInDatabase('things', ['email' => $this->email, 'random' => 'abc123']);

because I don't know what 'abc123' will be when using the actual random generator. So I created another implementation of the Random interface that always returns 'abc123' so I could assert against that.

Question is: how do I bind to this fake generator at testing time? I tried to do

$this->app->bind('Random', 'TestableRandom');

right before the test, but it still uses the actual generator that I register in AppServiceProvider. Any ideas? Am I on the wrong track completely regarding how to test such a thing?

Thanks!

like image 579
Lucio Assis Avatar asked Oct 13 '16 04:10

Lucio Assis


People also ask

What is the difference between service provider and service container in Laravel?

Service container is where your services are registered. Service providers provide services by adding them to the container.

What is the use of service container in Laravel?

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

What is service container binding in Laravel?

The Service Container in Laravel is a Dependency Injection Container and a Registry for the application. The advantages of using a Service Container over creating manually your objects are: Ability to manage class dependencies on object creation.

How is dependency injection implemented in Laravel?

By default, Laravel allows you to use a completely configuration-free dependency injection system. All you need to do is type hint the class that you want to inject – for example in your controller, middleware, jobs, event listeners, etc.


1 Answers

You have a couple options:

Use a conditional to bind the implementation:

class AppServiceProvider extends ServiceProvider {

    public function register() {
        if($this->app->runningUnitTests()) {
           $this->app->bind('Random', 'TestableRandom');
        } else {
           $this->app->bind('Random', 'RealRandom');
        }
    }
}

Second option is to use a mock in your tests

public function test_my_controller () {
    // Create a mock of the Random Interface
    $mock = Mockery::mock(RandomInterface::class);

    // Set our expectation for the methods that should be called
    // and what is supposed to be returned
    $mock->shouldReceive('someMethodName')->once()->andReturn('SomeNonRandomString');

    // Tell laravel to use our mock when someone tries to resolve
    // an instance of our interface
    $this->app->instance(RandomInterface::class, $mock);

    $this->post('/api/v1/do_things', ['email' => $this->email])
         ->seeInDatabase('things', [
             'email' => $this->email, 
             'random' => 'SomeNonRandomString',
         ]);
}

If you decide to go with the mock route. Be sure to checkout the mockery documentation:

http://docs.mockery.io/en/latest/reference/expectations.html

like image 170
scrubmx Avatar answered Sep 23 '22 17:09

scrubmx