Пополняемый список коротких ответов на вопросы по тестированию.
KernelTestBase
$modules, повторяем пока ошибки не пропадут.
class ExampleTest extends KernelTestBase {
public function test(): void {
// По FQCN (если у сервиса есть соответствующий alias)
$entity_type_manager = $this->container->get(\Drupal\Core\Entity\EntityTypeManagerInterface::class);
// Или по имени сервиса
$entity_type_manager = $this->container->get('entity_type.manager');
}
}class ExampleTest extends KernelTestBase {
use UserCreationTrait;
protected static $modules = [
'user',
];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
}
public function test(): void {
$admin_user = $this->createUser(
permissions: ['create page content', 'delete page content'], // Optional
name: 'John Smith', // Optional
values: ['mail' => 'john.smith@test.local'], // Optional
);
}
}Создание админа со всеми правами:
class ExampleTest extends KernelTestBase {
use UserCreationTrait;
protected static $modules = [
'user',
'system', // Обязательно
];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
}
public function test(): void {
$admin_user = $this->createUser(admin: TRUE);
}
}class ExampleTest extends KernelTestBase {
use UserCreationTrait;
protected static $modules = [
'user',
'system',
];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
}
public function test(): void {
$this->setUpCurrentUser(admin: TRUE);
...
}
}\Drupal\Tests\user\Traits\UserCreationTrait::setUpCurrentUser() или \Drupal\Tests\user\Traits\UserCreationTrait::setCurrentUser():
class ExampleTest extends KernelTestBase {
use UserCreationTrait;
protected static $modules = [
'user',
];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
}
public function test(): void {
$this->setUpCurrentUser(
values: ['name' => 'John Smith'], // Optional
permissions: ['create page content', 'delete page content'], // Optional
admin: FALSE, // Optional
);
// Или так, если пользователь уже есть
$this->setCurrentUser($user);
}
}class ExampleTest extends KernelTestBase {
use ContentTypeCreationTrait;
protected static $modules = [
'node',
];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('node');
$this->createContentType(['type' => 'page']);
}
public function test(): void {
// Стандартные вариант - через хелпер \Drupal\Core\Entity\EntityInterface::create()
$node = Node::create(['type' => 'page', 'title' => 'Test node']);
$node->save();
// Более правильный вариант - через \Drupal\Core\Entity\EntityStorageInterface
$node_storage = $this->container->get(EntityTypeManagerInterface::class)->getStorage('node');
$node = $node_storage->create(['type' => 'page', 'title' => 'Test node']);
$node_storage->save($node);
}
}Для многих сущностей есть тестовые фабрики, например для нод это \Drupal\Tests\node\Traits\NodeCreationTrait::createNode():
class ExampleTest extends KernelTestBase {
use ContentTypeCreationTrait;
use NodeCreationTrait;
protected static $modules = [
'node',
];
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('node');
$this->createContentType(['type' => 'page']);
}
public function test(): void {
$node = $this->createNode(values: ['type' => 'page']);
}
}Список некоторых фабрик из ядра:
- \Drupal\Tests\block\Traits\BlockCreationTrait::placeBlock()
- \Drupal\Tests\node\Traits\ContentTypeCreationTrait::createContentType()
- \Drupal\Tests\node\Traits\NodeCreationTrait::createNode()
- \Drupal\Tests\user\Traits\UserCreationTrait::createUser()
- \Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait::createVocabulary()
- \Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait::createTerm()
$this->config($config_name)->set($param_name, $param_value)->save():
class ExampleTest extends KernelTestBase {
public function setUp(): void {
parent::setUp();
$this->config('system.date')->set('timezone.default', 'UTC')->save();
}
}$this->setSetting($setting_name, $value):
class ExampleTest extends KernelTestBase {
public function test(): void {
$this->setSetting('file_public_path', 'files');
...
}
}$this->installSchema($module_name, $table_names):
class ExampleTest extends KernelTestBase {
public function setUp(): void {
parent::setUp();
$this->installSchema('MODULENAME', ['TABLE_NAME']);
}
}$this->installConfig($module_name):
class ExampleTest extends KernelTestBase {
protected static $modules = [
'system',
];
public function setUp(): void {
parent::setUp();
$this->installConfig('system');
}
}class ExampleTest extends KernelTestBase {
public function test(): void {
$entity_type_manager = $this->container->get(EntityTypeManagerInterface::class);
$view_builder = $entity_type_manager->getViewBuilder('ENTITY_TYPE_ID');
$entity = $entity_type_manager->getStorage('ENTITY_TYPE_ID')->load(123);
$build = $view_builder->view($entity);
$build = $view_builder->build($build);
...
}
}class ExampleTest extends KernelTestBase {
public function test(): void {
$build = [
'#prefix' => '<p>',
'#markup' => 'Hello, World!',
'#suffix' => '</p>',
];
$renderer = $this->container->get(\Drupal\Core\Render\RendererInterface::class);
$html = (string)$renderer->renderRoot($build); // "<p>Hello, World</p>!"
...
}
}$this->prophesize() или создания анонимного класса:
class ExampleTest extends KernelTestBase {
public function test(): void {
$path_matcher = $this->prophesize(PathMatcherInterface::class);
$path_matcher->isFrontPage()->willReturn(TRUE);
$this->container->set('path.matcher', $path_matcher->reveal());
...
// Или так
$path_matcher = new class() implements PathMatcherInterface {
public function matchPath($path, $patterns) {
return FALSE;
}
public function isFrontPage() {
return TRUE;
}
}
$this->container->set('path.matcher', $path_matcher);
...
}
}class ExampleTest extends KernelTestBase {
use \Drush\TestTraits\DrushTestTrait;
public function test(): void {
$this->drush(
command: 'my:command',
args: ['param1-value', 'param2-value'],
options: ['option1-name' => 'value1', 'option2-name' => 'value2'],
);
$this->assertOutputEquals('...');
}
}final class AgeCommandTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'MODULENAME',
];
/**
* {@selfdoc}
*/
public function test(): void {
$command = new AgeCommand($this->container->get(TimeInterface::class));
$tester = new CommandTester($command);
$tester->execute(['birth_date' => date('Y-m-d', strtotime('-18 years'))]);
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
self::assertSame('18', rtrim($tester->getDisplay()));
}
}final class ExampleTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'MODULENAME',
];
/**
* {@inheritDoc}
*/
protected function setUp(): void {
parent::setUp();
$this->container->get(ModuleHandlerInterface::class)->invoke('MODULENAME', 'install');
}
}BrowserTestBase
$this->drupalGet() посылает реальные http запросы с помощью Guzzle и curl. Запросы посылаются на домен, указанный в переменной окружения SIMPLETEST_BASE_URL.
$this->createUser() или его синонима $this->drupalCreateUser():
class ExampleTest extends BrowserTestBase {
public function test(): void {
// Создание обычного пользователя
$user = $this->createUser(
permissions: ['create page content', 'delete page content'], // Optional
name: 'Test user', // Optional
admin: FALSE, // Optional
values: ['mail' => 'test@test.test'], // Optional
);
// Создание админа со всеми правами
$admin = $this->createUser(admin: TRUE);
}
}class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->createRole(permissions: ['create page content'], rid: 'manager', name: 'Content manager');
$user = $this->createUser(values: ['roles' => 'manager']);
...
}
}BrowserTestBase тестах главный админ создаётся автоматически и доступен в $this->rootUser:
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalLogin($this->rootUser);
...
}
}$this->drupalLogin($user):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$user = $this->createUser();
$this->drupalLogin($user);
...
}
}class ExampleTest extends BrowserTestBase {
public function test(): void {
// Способ 1 - эмулирует выход используя браузер
$this->drupalLogout();
// Способ 2 - устанавливает текущим пользователем анонима
$this->drupalResetSession();
}
}$this->config($config_name)->set($param_name, $value)->save():
class ExampleTest extends BrowserTestBase {
public function setUp(): void {
parent::setUp();
$this->config('system.date')->set('timezone.default', 'UTC')->save();
}
}$this->writeSettings():
class ExampleTest extends BrowserTestBase {
public function test(): void {
$settings['settings']['hash_salt'] = (object) [
'value' => Crypt::randomBytesBase64(55),
'required' => TRUE,
];
$this->writeSettings($settings);
}
}Код пропишет в settings.php строку $settings['hash_salt'] = '...'; и пересоздаст сервис \Drupal\Core\Site\Settings
$this->placeBlock($plugin_id, $settings):
class ExampleTest extends BrowserTestBase {
public function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('page_title_block');
}
}$this->submitForm($form_data, $button):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->submitForm([
'Username' => 'admin',
'Password' => 'qwerty',
], 'Log in');
...
}
}В ключах массива можно передавать либо лейблы полей, либо их атрибут name.
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/path/to/form');
$this->submitForm([
'First checkbox' => 1, // Checked
'Second checkbox' => 0, // Unchecked
], 'Submit');
...
}
}$this->assertSession()->pageTextContains($text):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->assertSession()->pageTextContains('Log in');
}
}$this->assertSession()->elementTextContains($selectorType, $selector, $text):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->assertSession()->elementTextContains('css', 'h1', 'Log in');
}
}$this->assertSession()->statusMessageContains($text):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->submitForm([
'Username' => 'admin',
'Password' => 'qwerty',
], 'Log in');
$this->assertSession()->statusMessageContains('Unrecognized username or password');
}
}$this->assertSession()->elementExists($selector_type, $selector):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->assertSession()->elementExists('css', '.tabs--primary');
}
}$this->assertSession()->fieldExists($field):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->assertSession()->fieldExists('Username');
}
}В fieldExists() можно указывать лейбл поля, html id, либо html name.
$this->assertSession()->fieldValueEquals($field, $value):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->assertSession()->fieldValueEquals('Username', '');
}
}$this->assertSession()->elementAttributeContains():
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->assertSession()->elementAttributeContains(
selectorType: 'css',
selector: 'input[name="name"]',
attribute: 'autocomplete',
text: 'username',
);
}
}$this->assertSession()->addressEquals($path) или $this->assertSession()->addressMatches($regexp):
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$this->submitForm([
'Username' => 'admin',
'Password' => 'qwerty',
], 'Log in');
$this->assertSession()->addressEquals('/user/1');
// Или
$this->assertSession()->addressMatches('#^/user/\d+$#');
}
}$this->getSession()->getCurrentUrl():
class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->drupalGet('/user/login');
$current_url = $this->getSession()->getCurrentUrl(); // "https://example.com/user/login"
...
}
}\Drupal\Tests\language\Traits\LanguageTestTrait::createLanguageFromLangcode():
class ExampleTest extends BrowserTestBase {
use \Drupal\Tests\language\Traits\LanguageTestTrait;
public function test(): void {
$language = $this::createLanguageFromLangcode('ru');
...
}
}class ExampleTest extends BrowserTestBase {
public function test(): void {
$this->addTranslation('Home', 'Главная');
...
}
private function addTranslation(string $en_string, string $ru_string, string $context = ''): void {
$locale_storage = $this->container->get(\Drupal\locale\StringStorageInterface::class);
$string_object = $locale_storage->createString([
'source' => $en_string,
'context' => $context,
])->save();
$locale_storage->createTranslation([
'lid' => $string_object->lid,
'language' => 'ru',
'translation' => $ru_string,
])->save();
}
}entity_test:
class ExampleTest extends BrowserTestBase {
protected static $modules = [
'entity_test',
'user',
'field',
];
public function test(): void {
$entity_type_manager = $this->container->get(EntityTypeManagerInterface::class);
// Create field storage.
$field_storage = $entity_type_manager->getStorage('field_storage_config')->create([
'field_name' => 'my_integer_field',
'type' => 'integer',
'entity_type' => 'entity_test',
]);
$field_storage->save();
// Create field instance.
$field_instance = $entity_type_manager->getStorage('field_config')->create([
'field_storage' => $field_storage,
'label' => 'My integer field',
'bundle' => 'entity_test',
]);
$field_instance->save();
}
}Отдельного хэлпера для создания поля нет :( Можно использовать свой:
/**
* Create field.
*/
public function createField(
string $field_name,
string $field_type,
string $entity_type = 'entity_test',
string $entity_bundle = 'entity_test',
string $field_label = 'Test field',
array $storage_settings = [],
array $instance_settings = [],
?string $widget_type = NULL,
array $widget_options = [],
?string $formatter_type = NULL,
array $formatter_options = [],
): array {
$entity_type_manager = $this->container->get(EntityTypeManagerInterface::class);
$entity_display_repository = $this->container->get(EntityDisplayRepositoryInterface::class);
// Create field storage.
$field_storage = $entity_type_manager->getStorage('field_storage_config')->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => $field_type,
] + $storage_settings);
$field_storage->save();
// Create field instance.
$field_instance = $entity_type_manager->getStorage('field_config')->create([
'field_storage' => $field_storage,
'bundle' => $entity_bundle,
'label' => $field_label,
] + $instance_settings);
$field_instance->save();
// Configure widget.
if ($widget_type || $widget_options) {
if ($widget_type) {
$widget_options['type'] = $widget_type;
}
$entity_form_display = $entity_display_repository->getFormDisplay($entity_type, $entity_bundle);
$entity_form_display->setComponent($field_storage->getName(), $widget_options)->save();
}
// Configure formatter.
if ($formatter_type || $formatter_options) {
if ($formatter_type) {
$formatter_options['type'] = $formatter_type;
}
$entity_view_display = $entity_display_repository->getViewDisplay($entity_type, $entity_bundle);
$entity_view_display->setComponent($field_storage->getName(), $formatter_options)->save();
}
return [
'field_storage' => $field_storage,
'field_instance' => $field_instance,
];
}
Добавить комментарий