【问题标题】:How to execute a test controller (PHPUnit) that calls to the external API如何执行调用外部 API 的测试控制器 (PHPUnit)
【发布时间】:2020-05-22 01:52:51
【问题描述】:

我有 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()”时出现错误

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

我正在使用 Laravel 5.2.45

请帮帮我!

【问题讨论】:

  • 您可以创建一个模拟或检查您是否正在运行测试,然后省略 api 调用和检索 is。使用模拟可能是一个更好的选择,但我的建议是通过构造函数或作为方法参数注入 ApiModel - 这样您可以在测试时将其与模拟交换。你必须在容器上注册它。稍后我会尝试为您提供一些示例代码。

标签: php laravel api phpunit stub


【解决方案1】:

在 MyController 类上方添加 use APIModel;。需要是注册别名或者添加这个类的完整路径。

然后,在 GenId() 函数中添加return $response;

【讨论】:

  • 感谢您的回答。我的问题是不管$ apiModel-> GenId()的返回值如何运行phpunit还是随心所欲地控制API的返回值
【解决方案2】:

你可以通过服务提供者在容器中注册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,但是您可以将其更改为您需要的任何值。

更多关于嘲笑:Mocking Objects

或者,您可以简单地使用匿名类并用它替换容器上 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;
    }
});

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 2015-03-24
    • 2021-07-21
    • 2012-07-16
    • 2021-07-14
    相关资源
    最近更新 更多