首页 > 解决方案 > 如何执行调用外部 API 的测试控制器 (PHPUnit)

问题描述

我有 2 个文件: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;
       }
    }
}

现在我想为 MyController@post 执行 phpunit 测试

因为是运行phpunit,所以调用代码行“$apiModel->GenId()”时,出现错误

是否可以创建一个虚拟方法来调用一个虚拟方法而不是调用 $apiModel->GenId()?

我正在使用 Laravel 5.2.45

请帮我!

标签: phplaravelapiphpunitstub

解决方案


您可以做的是ApiModel使用服务提供者在容器中注册。

为此,请创建一个新的服务提供者 - 例如调用它ApiServiceProvider

php artisan make:provider ApiServiceProvider

我将假设您只ApiModel在几个特定的​​地方使用该实例,因此将推迟它的注册,直到它真正需要它。

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];
    }
}

现在,通过将实例注入ApiModel作为第二个参数来更新您的 post 方法

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(...);
    }
}

接下来,在您的测试中,将绑定替换为ApiModel

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

上面我指定该方法GenId应该返回1,但您可以将其更改为您需要的任何内容。

更多关于模拟:模拟对象

ApiModel或者,您可以简单地使用匿名类并用它替换容器上的实例

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

如果您希望 的其余功能ApiModel能够完全正常工作并且仅替换GenId方法,那么您可以扩展它

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

希望这可以帮助。


推荐阅读