Конечно, это заняло больше времени, чем ожидалось, но я достиг этой вехи, написав и выполнив полный функциональный тест в 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, нажмите кнопку «Связаться с нами» в любом месте на этом сайте, чтобы связаться.
.