Первый функциональный тест в Drupal 9 (тест перенаправления в контроллере)

Read this article in:

Конечно, это заняло больше времени, чем ожидалось, но я достиг этой вехи, написав и выполнив полный функциональный тест в Drupal. Это был непростой, тривиальный тест, и путь, который я выбрал для его реализации, подчеркивает трудности написания таких тестов. Кроме того, мой процесс, вероятно, не уникален, и проблемы, с которыми я столкнулся, кажутся общими. Это кажется ценным для меня, чтобы положить быстрый список причуд и трюков, которые я видел по пути. В этой статье я предполагаю, что читатель является промежуточным разработчиком Drupal. Я не буду углубляться в основы разработки Drupal.

* * * * * * *

Во-первых, это заняло у меня больше времени, чем я хотел бы признать. Существует большая разница между <?php и <?, последний является удобным сокращением для php-кода. Всегда рекомендую использовать первое. Причина в том, что исполняемый файл apache2 (или nginx) php настроен иначе, чем исполняемый файл php командной строки. Они настроены в этих двух файлах, соответственно:

  • /etc/php/8.1/cli/php.ini
  • /etc/php/8.1/apache2/php.ini

И даже если вы можете включить короткие теги:

short_open_tag = On

Вы можете забыть сделать это, или во множестве сред и серверов может быть просто неудобно и подвержено ошибкам устанавливать это дважды, каждый раз. Просто будьте осторожны, если ваш сайт отлично справляется с <? Это не означает, что дроссели или тесты будут работать вообще.

Распространенным признаком этой проблемы является то, что вы запускаете drush или phpunit, и вместо ожидаемого вывода вы видите свалку некоторого php-кода, особенно содержимого файла settings.php. Это означает, что php-интерпретатор не смог прочитать файл, поэтому ваши короткие теги не включены.

Теперь, когда это не так, давайте напишем код Drupal!

* * * * * * *

Сначала напишем производственный код, а тест - второй. Читателям, которые делают это наоборот: хорошо для вас, но я не нашел для этого особой пользы. На самом деле, если я застрял, я бы предпочел, чтобы производственный код работал, и я могу определить тесты позже. Если я застрял в тестах и производственный код не работает вообще - я бы сказал, что следующий шаг - переориентироваться и попытаться исправить ошибки и написать код, который работает - то есть производственный код. И да, иногда я пишу тестовый код перед производственным кодом. Но часто - нет.

Мы собираемся написать и протестировать простой контроллер, который перенаправляет URL-адрес шаблона. "/worklogs/{date}" соответствующего узла с field_date={date} Код довольно прост:

<?php
// web/modules/ish_drupal_module/src/Controller/WorklogsController.php

namespace Drupal\ish_drupal_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\node\Entity\Node;

class WorklogsController extends ControllerBase {

  public function showRedirect($year) {
    $nids = \Drupal::entityQuery('node')
      ->condition('field_date', $year)
      ->range(0, 1)
      ->execute();

    if (!empty($nids)) {
      $nid = reset($nids);
      $node = Node::load($nid);
      return new RedirectResponse($node->toUrl()->toString());
    }
    return new RedirectResponse('/worklog');
  }

}

Это входит в модуль, который вы, вероятно, уже установили. Мой модуль называется ish drupal module, и название следует за формализмом, который я повторно использую в другом месте.

* * * * * * *

Теперь проследим несколько итераций тестирования, первая из которых не сработала. Я начал со следующего теста:

<?php
// web/modules/ish_drupal_module/tests/src/Functional/WorklogsControllerTest.php

namespace Drupal\Tests\ish_drupal_module\Functional;

use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase;

class WorklogsControllerTest extends BrowserTestBase {
  protected $defaultTheme = 'stark';
  protected static $modules = ['node', 'ish_drupal_module', 'user'];
  protected $user;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    //                                    permissions, name, is_admin
    $this->user = $this->drupalCreateUser([], NULL, TRUE);
    $this->user->addRole('administrator');
  }

  /**
   * Tests the redirect
  **/
  public function testShowRedirect() {
    $node = Node::create([
      'type' => 'worklog',
      'title' => 'Test Node 2025a',
      'field_date' => '2025a',
      'status' => 1,
    ]);
    $node->save();
    $saved_node = Node::load($node->id());
    $this->assertNotNull($saved_node, 'Node was saved successfully.');

    $this->drupalLogin($this->user);
    $current_user = \Drupal::currentUser();
    $this->assertEquals($this->user->id(), $current_user->id(), 'User is logged in.');

    $this->assertSession()->addressEquals($node->toUrl()->toString());
    $this->assertSession()->statusCodeEquals(200);

    $this->assertSession()->pageTextContains('Test Node 2025a');
  }
}

Чтобы запустить его, мне пришлось установить кучу вещей через композитора.

Примечание: Я изменил минимальную стабильность с «стабильной» на «dev» в composer.json. Это не должно быть сделано в производстве.

Выдача комбинации любых или всех следующих команд для установки библиотек, в конечном итоге сработала для меня:

 composer require --dev --no-update symfony/filesystem:4.4.42
 composer require --dev drupal/core-dev:9.5.11 --with-all-dependencies
   composer require --dev behat/mink jcalderonzumba/mink-phantomjs-driver
   composer update -W
   composer update symfony/filesystem:4.4.42 drush/drush -W
   composer require --dev drupal/core-dev -W
   composer require --dev phpspec/prophecy-phpunit:^2

Очевидно, я скопировал файл phpunit.xml из ядра и настроил некоторые значения.

Я был готов провести тест следующим образом:

./vendor/bin/phpunit -c phpunit.xml  -d memory_limit=1G web/modules/ish_drupal_module/tests/src/Functional/WorklogsControllerTest.php --debug

Это не сработало — я получил 403 permission deniedВместо ожидаемого 200 okЯ добавил утверждения, что (1) узел сохранен, и (2) пользователь вошел в систему, чтобы убедиться, что я нахожусь в аутентифицированной среде.

Кстати, прошло утверждение, что узел спас, мне не помогло!

Затем я представил кучу журналов, чтобы понять, почему именно я получил код 403:


$response = $this->getSession()->getDriver()->getClient()->request('GET', '/worklogs/2025a', [], [], ['max_redirects' => 0]);
echo('+++ $response');
var_dump($response);

И это выявило проблему: поле 'field date' не присутствовало в тесте! В то время как поле присутствовало в производстве, производственная база данных не копируется в тестовую среду, поэтому поля, созданные в пользовательском интерфейсе, недоступны.

Следующим и последним шагом было добавление необходимого поля в тест, и после этого мой тест прошел:

  protected function setUp(): void {
    parent::setUp();

    if (!NodeType::load('worklog')) {
      NodeType::create([
          'type' => 'worklog',
          'name' => 'Worklog',
      ])->save();
    }
    if (!FieldStorageConfig::loadByName('node', 'field_date')) {
      FieldStorageConfig::create([
        'field_name' => 'field_date',
        'entity_type' => 'node',
        'type' => 'string',
      ])->save();
    }
    if (!FieldConfig::loadByName('node', 'worklog', 'field_date')) {
      FieldConfig::create([
        'field_name' => 'field_date',
        'entity_type' => 'node',
        'bundle' => 'worklog',
        'label' => 'Date',
      ])->save();
    }
    
    ...
  }

.

И весь мой тестовый файл выглядел так: https://github.com/wasya-co/ish drupal module/blob/0.5.0/tests/src/Functional/WorklogsControllerTest.php

Я надеюсь, что это поможет кому-то лучше развиваться в Drupal! Наконец, я доступен для работы с Drupal на основе проекта, поэтому, если вам нужна какая-то разработка Drupal, нажмите кнопку «Связаться с нами» в любом месте на этом сайте, чтобы связаться.

.

Please login to post comments: