Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
Всем привет,
В Laravel 5.5/vue.js2.5 приложении делаю проверку CRUD операций с помощью Dusk для таблицы document_categories:
public function testExample()
{
$site_name = Settings::getValue('site_name', '');
// try {
// DB::beginTransaction();
$document_category_id= '';
$this->browse(function (Browser $browser) use ($site_name, $document_category_id) {
$browser->resize(1920, 1080); // Width and Height
$new_document_category = 'new document category created at ' . time();
$browser->loginAs(User::find(5))
->visit('/admin/dashboard#/admin/document_categories/edit/new')
->type('#name', $new_document_category)// Поле заполняется ->type , но НЕ заполняется если ->value
->select('#type', 'D')// Enum field
->type('#document_category_content', $new_document_category . ' content lorem...')// textarea input
->click('.editor_button_submit')
->waitUntilMissing('.editor_button_submit')
->assertTitle('Document Categories of ' . $site_name)
// ->assertPathIs('/admin/dashboard#/admin/document_categories')
;
$document_category_id= DB::getPdo()->lastInsertId();
echo ' New document category id::'.print_r($document_category_id,true);
});
echo ' New document category id::'.print_r($document_category_id,true);
/* } catch (Exception $e) {
DB::rollBack();
throw $e;
}
DB::commit();*/
}
Новая строка вставляется, но:
1) Кроме добавления строки хочу по $document_category_id эту строку отредактировать и удалить для чего погуглив в коде выше
считываю
DB::getPdo()->lastInsertId();
но в консоль выводится 0. Тут нужно как-то иначе получить lastInsertId ?
Если раскоментарить блоки транзакции - то также получаю 0.
2) Это Vuejs 2.5 приложение с настройкой рутов:
const routes = [
{
path: '/',
...
{path: '/admin/document_categories', component: DocumentCategoriesIndex, name: 'documentCategoriesIndex'},
{path: '/admin/document_categories/edit/:id', component: DocumentCategoryEdit, name: 'editDocumentCategory'},
...
]
const router = new VueRouter( {
mode: 'hash', // default
routes
})
И при открытии формы для создания открывается урл :
http://local-tasks.com/admin/dashboard#/admin/document_categories/edit/new
И при сохранении переходит на
http://local-tasks.com/admin/dashboard#/admin/document_categories
И на последний assertPathIs получаю ошибку :
Tests\Browser\ExampleTest::testBasicExample
Failed asserting that '/admin/dashboard' matches PCRE pattern "/^\/admin\/dashboard#\/admin\/document_categories$/u".
Почему ошибка и как ее исправить ?
3) Кто в курсе реализации тестов Dusk-а подскажите а стоит ли заворачивать эти тесты в блоки транзакции как закоментировано выше ?
Спасибо !
Не в сети
Дружище, привет. замечаний будет много
Первое - в тестах нужно использовать чистую отдельную БД и использовать трейт RefreshDatabase. это для того, чтобы А) - не засунуть/удалить по ошибке лишних данных в основную БД, Б) сделать жизнь проще. ларавел позволяет это сделать очень просто. в phpunit.xml, в тэге php добавь следующие записи
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
то есть будешь использовать sqlite базу подгружаемую в память. можно конечно и настроить отдельное MySql соединение в database.php для тестирования. но такой метод проще и в 95% случае именно то, что нужно
Второе, как уже сказал, заюзай трейт RefreshDatabase и убери все DB::beginTransaction rollback и коммит.
Третье - Теперь, когда у тебя чистая база, ты легко можешь сделать утверждения на счет того, что ты ожидаешь по результатам своего теста. Например, я так понимаю, что при сабмите формы кнопкой .editor_button_submit должна создаться новая запись в... DocumentCategory? ок, если так, наверно это у тебя модель? тогда ты можешь сделать следующее:
$this->assertEquals(1, DocumentCategory::count());
все - ты протестировал что у тебя создалась новая запись в DocumentCategory. Ты можешь быть в этом уверен, потому что ты начинал с чистой базы, и у тебя не было ни одной записи ни в DocumentCategory, ни в какой либо другой модели. Так же, ты можешь добавить утверждений
$documentCategory = DocumentCategory::first(); // взяли вновь созданную модель, тк она одна в базе
$this->assertEquals("D", $documentCategory->type); // проверяем, что то, что юзер ввел в форме, то и сохранилось в модель
$this->assertEquals("content lorem...", $documentCategory->content); // чтобы это проверить, избавься от time() - он не нужен тебе
$this->assertRedirect('uri');
ну и напоследок, dusk - не самый оптимальный выбор для тестирования поведения моделей. гораздо лучше тестировать на http уровне, что ларавел тоже делает очень простым и приятным занятием
$user = factory(User::class)->create();
$this->actingAs($user)->post('url куда уходить пост запрос',[
'type' => 'D',
'content' => 'Lorem ipsum...'
]);
Пишу бэкенд для своего мобильного приложения, делюсь с миром, заходите посмотреть, много всего интересного https://youtu.be/C5M6-ycJ5gs
Не в сети
Спасибо за развернутый коментарий - очень полезно!
1)Но если юзать отдельную БД (не рабочую) и использовать трейт RefreshDatabase - не есть ли это излишне ?
2) Да и я бы предпочел 1 строку создать отредактировать и следом удалить(да и клиент так пишет) позтову вопрос почему не работает
DB::getPdo()->lastInsertId();
по-прежнему актуален
3) А чем http теcтирование лучше? Ведь при таком тестировании проверяются только контрол и модел?
А в случае Dusk-а и view. Или нет?
4) Клиент хочет сделать несколько файлов тестов чтобы их кроном запускать ночью(меньше нагрузка)
Можно ли получить результаты выполнения(кол-во успешных тестов и ошибок) и выслать их мейлом на админа?
Приходит мысль делать буферизацию и парсить оутпут, нет ли способа лучше?
Не в сети
кроном тесты на продакшене? но... зачем? нет, так не делают) зависимости для тестирования даже установлены в require-dev в композере - они по умолчанию на продакш не ставятся. Флоу: ты разрабатываешь фичу, используя тесты. когда твои тесты зеленые, ты можешь быть уверен, что твоя фича работает так как нужно. ты деплоишь код в продакшн. код в продакшене лежит протестированный и работает. и новый код туда не попадает, покуда твои тесты вновь зеленые - и так по кругу. так это работает. По вопросу конкретно, я не в курсе как можно получить отчет о результатах тестирования. Но блин! даже сама идея противоречивая))) то есть, с таким подходом получается, что ты допускаешь, что в продакшене будут фейлиться тесты! но так нельзя, код не может попасть в продакшн, если тесты фейлятся.
1 - нет, это не излишне, ни в коем случае. это делает твою жизнь проще, а в этом и суть тестов. когда у тебя в базе полно данных, ты не можешь наверняка составить ассерты
2) - но это три разных действия, должно обрабатываться трямя разными эндпоинтами, должны иметь три (как минимум своих теста)
3) тестирование на http уровне дает тебе намного более детальное представление о том что происходит, где что фейлится, где рушится логика, ты видишь как легко продвигаешься по стеку исключений от ошибки к ошибке. даск просто дает финальный фейл (как в твоем примере) и не понятно на каком этапе что сломалось. погляди мои видео https://www.youtube.com/watch?v=pUV_tVQlsBE - я сейчас пишу бэкенд для апи для своего приложения, делюсь тем что есть. круто что ты пытаешься внедрить тесты в свое воркфлоу, это правильный путь)
Вьюхи тоже можно нужно тестировать http уровне. допустим ты тестируешь листинг товаров. в фазе сетапа теста ты создаешь несколько товаров, (при этом создаешь их с такими параметрами, как нужно тебе, что опять же невозможно работая с нечистой базой). в фазе экшена делаешь гет запрос на эндпоинт. проверяешь что получил ответ такой то, урл у тебя такой то, проверяешь что вьюха получила необходимый набор из созданных тобою товаров. $this->assertViewHas. и все. а тестировать местоположение элементов на страницы - ну это такое. это значит что если ты решил изменить слегка дизайн, или например изменить формат даты во вьюхе, или используешь другой css фреймвор и классы твоих элементов поменялись - твои даск тесты упадут. будет ли это означать что твое приложение перестало работать? нет. ты просто поменял дизайн. для чего тогда тесты, если они не тестируют поведение, а ломаются от смены дизайна.
Бомбануло чот))
Изменено code_bright_anywhere (14.03.2018 21:10:07)
Пишу бэкенд для своего мобильного приложения, делюсь с миром, заходите посмотреть, много всего интересного https://youtu.be/C5M6-ycJ5gs
Не в сети
Спасибо, еще раз!
1) А что это за метод assertEquals
Тут https://laravel.com/docs/5.5/dusk#available-assertions я такого не нашел ...
И чем его заменить - типа данное условие истинно ?
2) Пока с клиентом речь идет что будет одно приложение(поэтому тесты запускать ночью), но с двумя базами, поэтому я полагаю сделать в .env :
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tasks
DB_DUSK_DATABASE=tasks_dusk
DB_USERNAME=root
DB_PASSWORD=1
И в config/database.php :
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
],
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => 'tsk_',
'strict' => true,
'engine' => null,
],
'testing' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DUSK_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => 'tsk_',
'strict' => true,
'engine' => null,
],
И для тестирования будет взята база данных из 'testing' ?
3) И как сделать тестируемую базу ? Из рабочей очистить таблицы CRUD-ы к кторым будут тестироваться ? Еще есть нюансы?
Не в сети
После того, как ты так настроил в database.php, в phpunit.xml пропиши <env name="DB_CONNECTION" value="testing"/> и тогда в тестах будет использоваться тестовая база. для сетапа ничего делать не нужно, твой трейт RefreshDatabase будет это делать сам - а именно, накатывать миграции перед каждым тестом.
AssertEquals это базовый ассерт предоставляемый phpunit, по ссылке что у тебя это конкретно даск ассерты, надстроенные уже над phpunit и расширяющие его возможности. ты легко можешь использовать все ассерты из phpunit в своих даск тесах, что очень круто. /vendor/phpunit/phpunit/src/Framework/Assert.php - здесь можно посмотреть все ассерты phpunit, ну или в их документации
Данное условие истинно - есть ассерт $this->assertTrue($value). или $this->assertNotNull($value). соответственно есть и обратные, $this->assertFalse and $this->assertNull()
Пишу бэкенд для своего мобильного приложения, делюсь с миром, заходите посмотреть, много всего интересного https://youtu.be/C5M6-ycJ5gs
Не в сети
А как прапвильно подключить, получаю ошибку
BadMethodCallException: Call to undefined method [assertEquals].
?
Я нашел
use PhpUnit\Framework;
use Tests\TestCase;
но не помогает...
Не в сети
твои даск тесты должны наследовать базовый DuskTestCase, который в свою очередь наследует PHPUnitTestCase. и все ассерты от туда доступны в твоем даск тесте. у тебя так? создавал тест командой artisan dusk:make?
Пишу бэкенд для своего мобильного приложения, делюсь с миром, заходите посмотреть, много всего интересного https://youtu.be/C5M6-ycJ5gs
Не в сети
Пересоздал еще раз:
$ php artisan dusk:make documentCategoriesCRUD
Test created successfully.
И получаю файл tests/Browser/documentCategoriesCRUD.php:
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class documentCategoriesCRUD extends DuskTestCase
{
/**
* A Dusk test example.
*
* @return void
*/
public function testExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertSee('Laravel');
});
}
}
И в tests/DuskTestCase.php :
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless'
]);
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}
}
Если поменять вручную
abstract class DuskTestCase extends PHPUnitTestCase
То ошибка что PHPUnitTestCase не найден и как его подключить ?
Имею :
$ php artisan --version
Laravel Framework 5.5.35
В composer.json :
"require-dev": {
...
"phpunit/phpunit": "~6.0"
...
},
?
Не в сети
code_bright_anywhere, не мог бы выложить заголовки своих тестов с использованием методов assertEquals, assertStatus ?
По-прежнему ищу решение...
Не в сети
привет, сори за длительный ответ
что фейлится при таком сетапе?
из документации все шаги выполнил? провайдер зарегистрировал?
в composer.json имеется
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
Пишу бэкенд для своего мобильного приложения, делюсь с миром, заходите посмотреть, много всего интересного https://youtu.be/C5M6-ycJ5gs
Не в сети