NewtSullivan's avatar

Creating Test for service with Contextual Binding

I created a TemplateServiceInterface that is being used by my EmailTemplateService and SmsTemplateService. They are both being used in different controller. the problem was when I was testing the EmailTemplateService, Im getting an error of Target [App\Contracts\TemplateServiceInterface] is not instantiable.

here's my test


class EmailTemplateServiceTest extends TestCase
{
    use RefreshDatabase;

    private $user = null;

    private $service = null;

    /**
     * test that the service can return available email templates
     */
    public function testServiceCanReturnAvailableEmailTemplates()
    {
        $this->prepare();

        $templates = $this->service->getAvailablePublicTemplates($this->user->user_code);
        $this->assertCount(0, $templates);
    }

    /**
     * Prepares the test environment with authentication
     */
    public function prepare($abilities = [])
    {
        $this->withoutExceptionHandling();

        $this->service = app()->make(TemplateServiceInterface::class);
        $this->assertInstanceOf(\App\Services\EmailTemplateService::class, $this->service);

        $this->user = User::factory()->create();

        Sanctum::actingAs(  $this->user, $abilities );
    }
}

on my AppServiceProvider.php I currently have this line.

        $this->app->when(EmailTemplateController::class)
            ->needs(TemplateServiceInterface::class)
            ->give(function($app) {
                return new EmailTemplateService();
            });

I haven't created the binding for the SmsTemplateService yet. I did try to change the binding to the test class but to no avail.

        $this->app->when(EmailTemplateServiceTest::class)
            ->needs(TemplateServiceInterface::class)
            ->give(function($app) {
                return new EmailTemplateService();
            });

Can anyone tell me how? thanks

0 likes
2 replies
LaryAI's avatar
Level 58

You can use the resolve() method to manually resolve the service in your test. This will allow you to inject the service into your test without having to create a contextual binding.

class EmailTemplateServiceTest extends TestCase
{
    use RefreshDatabase;

    private $user = null;

    private $service = null;

    /**
     * test that the service can return available email templates
     */
    public function testServiceCanReturnAvailableEmailTemplates()
    {
        $this->prepare();

        $this->service = $this->app->resolve(TemplateServiceInterface::class);
        $this->assertInstanceOf(\App\Services\EmailTemplateService::class, $this->service);

        $templates = $this->service->getAvailablePublicTemplates($this->user->user_code);
        $this->assertCount(0, $templates);
    }

    /**
     * Prepares the test environment with authentication
     */
    public function prepare($abilities = [])
    {
        $this->withoutExceptionHandling();

        $this->user = User::factory()->create();

        Sanctum::actingAs(  $this->user, $abilities );
    }
}
NewtSullivan's avatar

@LaryAI Got an error saying I was calling a protected method

Call to protected method Illuminate\Foundation\Application::resolve() from scope Tests\Feature\emailtemplate\EmailTemplateServiceTe
st

Please or to participate in this conversation.