Yii2 codeception REST API mock external API?

Ссылка скопирована
1 ответ

Приветствую всех!

Есть проблема, при написание тестов столкнулся с необходимостью мокать сторонние API сервисы чтобы они зря каждый раз не вызывались, суть в чем:

Это кусок кода из тестов:

public function signIn(ApiTester $I)     {         $data = [             'phone' => '79999999999',              'token'  => '000000',         ];          $jsonData = json_encode($data);          $I->sendPost('/sign-in', $jsonData);          $I->seeResponseCodeIs(200);         $I->seeResponseIsJson();         $I->seeResponseMatchesJsonType([             'token'      => 'string',             'expires_at' => 'integer',         ]);     }

public function signIn(ApiTester $I) { $data = [ 'phone' => '79999999999', 'token' => '000000', ]; $jsonData = json_encode($data); $I->sendPost('/sign-in', $jsonData); $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); $I->seeResponseMatchesJsonType([ 'token' => 'string', 'expires_at' => 'integer', ]); }

На эндпоинте котором проводится тест (/sign-in) есть запросы в другие API, к примеру получить данные по номеру телефона или отправить СМС код на телефон и т.п., необходимо все сторонние запросы замокать чтобы во время тестов ничего левого не вызывалось.

Дополнительно:

Ответы:

Простой вариант (костыли):

Добавить в функциях, которые ожидают ответ от внешних сервисов, исключения для тестов

if (YII_ENV_TEST) {     return true; }

if (YII_ENV_TEST) { return true; }

Правильный вариант:

Зарегистрировать сервис внешних обращений в конфиге, в коде пользоваться только сервисом из контейнера Yii::$app->apiService

Cоздать загрулшку и зарегистрировать ее в качестве этого сервиса в тестовом конфиге

# config/test.php  $config =  yiihelpersArrayHelper::merge(     require(__DIR__ . '/web.php'),     [         'id' => 'app-tests',         'components' => [             'apiService' => [                 'class' => 'AppFakeApiService',             ],         ],     ], ); return $config;

# config/test.php $config = yiihelpersArrayHelper::merge( require(__DIR__ . '/web.php'), [ 'id' => 'app-tests', 'components' => [ 'apiService' => [ 'class' => 'AppFakeApiService', ], ], ], ); return $config;

Тестовый конфиг использовать в настройках модуля Codeception

# tests/integration.suite.yml  class_name: IntegrationTester modules:     enabled:       - Asserts       - Yii2:             part: [orm, email, fixtures]             configFile: 'config/test.php'

# tests/integration.suite.yml class_name: IntegrationTester modules: enabled: - Asserts - Yii2: part: [orm, email, fixtures] configFile: 'config/test.php'

  • К сожалению в компонент я уже не могу сделать (переделать), в данный момент все сделано по модулям, т.е. 1 апи это отдельный модуль, я так понял что по вашему предложению я могу его использовать даже для модулей

    'modules' => []

  • NEATweb, можно попробовать заменить модуль по той же схеме, но предназначение у модулей другое, как-то не правильно сделано, на мой взгляд. Если только пользователям не нужно напрямую в эти сервисы обращаться.
  • Виктор, проблема в том, что модуль API один, а в нём идут классы API:
    'modules' => [  'class' => 'API' -> здесь используется специальная библиотека для работы с API  'controllerMap' => [    'api1' => [     'class' => '...'    ],    'api2' => [     'class' => '...'   ]  ] ]

    'modules' => [ 'class' => 'API' -> здесь используется специальная библиотека для работы с API 'controllerMap' => [ 'api1' => [ 'class' => '...' ], 'api2' => [ 'class' => '...' ] ] ]

    и потом все это вызывается как $this->module->runAction('API/classname/method'). И соответственно переделывать всё это займет уйму времени, да и никто не даст менять всю структуру (на данный момент точно)

  • NEATweb, сделано как костыли, значит остается эти костыли модифицировать
    if (!YII_ENV_TEST) {     $this->module->runAction('API/classname/method'); }

    if (!YII_ENV_TEST) { $this->module->runAction('API/classname/method'); }

  • Виктор, такой вариант после вашего первого ответа мне в голову и пришел, но не хочется погибать в костылях дальше, буду думать может быть есть возможность какая-то...
  • NEATweb, $this->module->runAction('API/classname/method'); это все-равно что метод контроллера где-нибудь вызвать.

    Хотя да, есть еще вариант, сделать все-таки сервис, внутри него спрятать это $this->module->runAction('API/classname/method'); и сделать тестовый сервис)

  • Виктор, т.е. получается если я в test.php добавлю модули как в обычном конфиге, но сделаю для них fake controllers которые условно всегда будут возвращать true, так не получится, потому что изначально вызывается на прямую определённый action из определённого места, test.php не переопределяет же основной конфиг...
  • NEATweb, переопределяет, ArrayHelper заменит модуль по ключу
  • Виктор, по ключу т.е. по id => 'app-tests'?
  • Виктор, теперь понял для чего нужен require(__DIR__ . '/web.php'), чтобы переопределить основной конфиг и создать новый экземляр yii и использовать тестовые настройки
  • NEATweb, нет, это id приложения.

    Теоретически так

    $config =  yiihelpersArrayHelper::merge(     require(__DIR__ . '/web.php'),     [         'id' => 'app-tests',         'modules' => [             'class' => 'FAKE API' -> здесь НЕ используется специальная библиотека для работы с API             'controllerMap' => [             'api1' => [                 'class' => '...'             ],             'api2' => [                 'class' => '...'             ]         ]     ], ); return $config;

    $config = yiihelpersArrayHelper::merge( require(__DIR__ . '/web.php'), [ 'id' => 'app-tests', 'modules' => [ 'class' => 'FAKE API' -> здесь НЕ используется специальная библиотека для работы с API 'controllerMap' => [ 'api1' => [ 'class' => '...' ], 'api2' => [ 'class' => '...' ] ] ], ); return $config;

  • NEATweb, но лучше все-же сделать вместо $this->module->runAction('API/classname/method'); некий сервис с методом, внутри которого будет эта хрень, зарегистрировать его в компонентах и использовать вместо этого вызова модуля. А для тестов сделать фейковую реализацию этого компонента.
  • Виктор, да, я тоже так думаю, спасибо большое за четкие разъяснения, если что напишу ещё раз :))
Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Роман IT Ответ

Для мокирования внешнего API в тестах с использованием Yii2 и Codeception, можно воспользоваться встроенными возможностями Codeception для работы с REST API.

Для начала, необходимо установить Codeception, если он еще не установлен. Далее, создайте тестовый класс для работы с REST API, например, `ApiCest`.

```php

use Codeception\Util\HttpCode;
 
class ApiCest
{
    public function _before(ApiTester $I)
    {
        // настройки перед каждым тестом
    }
 
    public function _after(ApiTester $I)
    {
        // настройки после каждого теста
    }
 
    public function testExternalApi(Mockery $I)
    {
        $I->wantTo('mock external API response');
 
        $I->haveHttpHeader('Content-Type', 'application/json');
        $I->sendGET('/api/external-endpoint');
 
        $I->seeResponseCodeIs(HttpCode::OK);
        $I->seeResponseContainsJson([
            'key' => 'value'
        ]);
    }
}

use Codeception\Util\HttpCode; class ApiCest { public function _before(ApiTester $I) { // настройки перед каждым тестом } public function _after(ApiTester $I) { // настройки после каждого теста } public function testExternalApi(Mockery $I) { $I->wantTo('mock external API response'); $I->haveHttpHeader('Content-Type', 'application/json'); $I->sendGET('/api/external-endpoint'); $I->seeResponseCodeIs(HttpCode::OK); $I->seeResponseContainsJson([ 'key' => 'value' ]); } }

```

В приведенном примере, мы создали тестовый метод `testExternalApi`, который отправляет GET запрос на внешний API и проверяет ответ.

Для мокирования ответа в тесте, можно использовать библиотеку Mockery. Пример:

```php

public function testExternalApi(Mockery $I)
{
    $I->wantTo('mock external API response');
 
    $mockedResponse = [
        'key' => 'value'
    ];
 
    $I->haveHttpHeader('Content-Type', 'application/json');
    $I->haveHttpHeader('X-Mock-Response', json_encode($mockedResponse));
    $I->sendGET('/api/external-endpoint');
 
    $I->seeResponseCodeIs(HttpCode::OK);
    $I->seeResponseContainsJson($mockedResponse);
}

public function testExternalApi(Mockery $I) { $I->wantTo('mock external API response'); $mockedResponse = [ 'key' => 'value' ]; $I->haveHttpHeader('Content-Type', 'application/json'); $I->haveHttpHeader('X-Mock-Response', json_encode($mockedResponse)); $I->sendGET('/api/external-endpoint'); $I->seeResponseCodeIs(HttpCode::OK); $I->seeResponseContainsJson($mockedResponse); }

```

В данном примере, мы создали мокированный ответ и передали его в заголовке `X-Mock-Response`. При отправке запроса на внешний API, будет использован мокированный ответ вместо реального.

Таким образом, вы можете использовать Codeception и Mockery для мокирования внешнего API в ваших тестах с помощью Yii2.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно