0
votes

I have got 2 files: MyController.php, APIModel.php

MyController.php

class MyController extends Controller
{
    public function post(Request $request)
    {
        $apiModel = new APIModel;                 
        $apiResponse = $apiModel->GenId();
        // Other process such as get data from database, validate data
        return view(...);
    }
}

APIModel.php

class APIModel extends Model
{
   function GenId()
   {
        $response = $this->callAPI('GET', config('services.api_url.API_URL_GENID'));
   }

   function callAPI($method, $url, $data = array())
   {
       $curl = curl_init();

       ...

       try
       {
            $result = curl_exec($curl);
            if (!$result) {
                throw new ServiceUnavailableHttpException('Could not connect to interface web service.');
            }
       }
       catch (ServiceUnavailableHttpException $e)
       {
           throw $e;
       }
    }
}

Now I want to perform a phpunit test for MyController@post

Because it is running phpunit, when calling to the code line "$apiModel->GenId()", the error occurs

Is it possible to create a dummy method to call a dummy method instead of calling $apiModel->GenId()?

I'm using Laravel 5.2.45

Please help me!

2
You could create a mock or check if you’re running tests and then omit the api call and retrieval of the is. using mock would probably be a better option, but my suggestion would be to inject the ApiModel via constructor or as a method argument - this way you can swap it with a mock when testing. You’d have to register it on the container. I’ll try to get you some example code in a moment.Sebastian Sulinski

2 Answers

1
votes

What you could do is to register ApiModel in the container by using service provider.

To do it, create a new service provider - call it for instance ApiServiceProvider

php artisan make:provider ApiServiceProvider

I'm going to assume you only use the ApiModel instance in a few specific places, therefore will defer its registration until it's actually needed.

use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;

class ApiServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(APIModel::class, function () {
            return new APIModel;
        });
    }

    public function provides(): array
    {
        return [APIModel::class];
    }
}

Now, update your post method by injecting instance of ApiModel to it as a second argument

class MyController extends Controller
{
    public function post(Request $request, APIModel $client)
    {
        $apiResponse = $client->GenId();
        // Other process such as get data from database, validate data
        return view(...);
    }
}

Next, in your test replace the binding with a mock of the ApiModel

$this->mock(APIModel::class, function ($mock) {
    $mock->shouldReceive('GenId')->once()->andReturn(1);
});

Above I'm specifying that method GenId should return 1, but you can change it to whatever you need it to be.

More on mocking: Mocking Objects

Alternatively you could simply use anonymous class and replace the instance of the ApiModel on the container with it

$this->instance(APIModel::class, new class {
    public function GenId()
    {
        return 1;
    } 
});

If you'd like the remaining functionality of the ApiModel to be fully working and only replace GenId method then you could extend it

$this->instance(APIModel::class, new class extends ApiModel {
    public function GenId()
    {
        return 1;
    }
});

Hope this helps.

0
votes

add use APIModel; above MyController class. Need to be a registered alias or otherwise add the full path of this class.

Then, add a return $response; in GenId() function.