LINUX.ORG.RU

Codeception действительно такое дерьмо каким кажется?

 , , ,


0

1

Закинула меня судьбинушка заниматься тестированием на PHP. В компании уже немного укоренился Codeception. И вот о нём хочу спросить, оно действительно настолько дерьмово или я просто не умею пользоваться этой замечательной программой?

Претензия номер раз. Идея сделать код близким к человеческому языку не так хороша как может показаться. Вот пример из статьи на Хабре:

<?php
$I = new TestGuy($scenario);
$I->wantTo('create new blog post');
$I->amOnPage('/blog/posts');
$I->click('Create new post');
$I->fillField('Title','Codeception, a new way of testing!');
$I->fillField('Text','Codeception is new PHP full-stack testing framework.');
$I->click('Send');
$I->see('Congratulations, your post is successfully created!');
Выглядит круто. Но ближе к реальным тестам всё начнёт расползаться. Вот один из первых примеров из документации:
<?php
$I = new FunctionalTester($scenario);
$I->amOnPage('/');
$I->click('Sign Up');
$I->submitForm('#signup', ['username' => 'MilesDavis', 'email' => 'miles@davis.com']);
$I->see('Thank you for Signing Up!');
$I->seeEmailSent('miles@davis.com', 'Thank you for registration');
$I->seeInDatabase('users', ['email' => 'miles@davis.com']);
Уже пошли какие-то словари и массивы. В реальных тестах всё ещё хуже, ещё больше тест напоминает PHP и ещё меньше человеческий язык (на самом деле вообще не напоминает). То есть codeception'овые тесты не могут просматриваться, редактироваться, оцениваться, критиковаться, дополняться и т.п. никем кроме PHP-программистов. Идея теста на человеческом языке в таком виде провалилась.

Понимая это, в Codeception добавили возможность исполнения Gherkin-файлов. То есть сделали Cucumber/Behat (официальная реализация Cucumber'а на PHP), но с неповторимым вкусом говна. Мало того, что Codeception не поддерживает feature-файлы на языке отличном от английского (для менеджеров я бы хотел писать на русском), так ещё и в Codeception позабыли зачем хотели иметь тесты на человеческом языке. При исполнении gherkin-файлов в консоль не выводятся пройденные шаги. Более того даже с -v не выводятся. Step'ы будут выводится начиная с уровня -vv, а в какой строке в них случился exception только на уровне -vvv. То есть вывести в консоль сколько тестов прошло, сколько завалилось — это важно, а что при это делалось, в каких местах тесты падали — это совершенно не важно.

Далее. Я прочёл пару статей в защиту Codeception'а, которые даже размещены на сайте Codeception'а. Первая и вторая. Обе показались каким-то бредом, в котором всё перевёрнуто с ног на голову. Якобы код на codeception'е проще писать, потому что есть автодополнение. Аллё, feature-файлы вообще пишутся на естественном языке. Если вы не можете писать по-английски чего вам надо, то никакой autocomplete вам не поможет. А если вам нужно что-то за рамками стандартных функций Codeception'а, то опять же отсосайтунг, автокомплит не поможет. А нужно это часто. Авторы codeception'а рассчитывают, что стандартные функции покрывают 90% случаев. Хрен-то там, я бы не дал больше 50%. Да и те тривиальны, элементарно и быстро реализуются step'ами Behat'а.

Во второй статье автор тоже попытался всё вывернуть наизнанку

Don’t fall into a marketing trap. You will find yourself writing tests two times: as a feature in plain English and in code with PHP.

Это же и есть самое большое преимущество Behat'а и Cucumber'а! Кто-то, менеджер, product owner, другой программист или тестировщик может написать test case или usage scenario, а потом кто-то другой может это перевести с человеческого на машинный. Другими словами, один человек написал тест на Gherkin'е, и потом может другому объяснить что же в нём происходит. Точнее даже пояснять не придётся. Если написано «Кладу в блендер 1 банан/ Кладу в блендер 5 клубничин/Кладу в блендер стакан йогурта/Вижу в блендере смузи», то это и происходит, это и проверяется. Неподготовленные люди могут писать feature-файлы, но никто не станет писать тест на codeception/PHP. Cucumber позволяет иметь хоть какое-то описание чего же всем этим людям надо, а с codeception'ом тесты это вольная фантазия автора, и всякому ещё нужно будет пояснять что же в этих тестах происходит, проверяется и какой test case покрывает.

Так поясните же мне кто понимает, чем же этот codeception хорош? Кто в здравом уме станет им пользоваться?

★★★★★

автор написал некую прогу и хочет её монетизировать. возможно он даже думает, что она действительно нагибает.

ckotinko ☆☆☆ ()

Буду краток: учи английский, пригодится.

bitfroster ★★ ()
Ответ на: комментарий от ckotinko

Всё гораздо хуже

автор написал некую прогу и хочет её монетизировать. возможно он даже думает, что она действительно нагибает.

Не он один так думает. Вон, на Хабре человек статью написал, в англоязычном интернете часто упоминается.

Ещё, кстати, о сравнении Codeception'а и Behat'а.

https://toster.ru/q/245540

Если ваша команда записывает требования в терминах Given-When-Then и загоняется по BDD, то выбор очевиден - Behat. Есть еще команды которые заставляют писать функциональные тесты своих тестировщиков и для этого используют gherkin-сценарии. Если вам надо просто покрыть все функциональными тестами то Codeception (или любой другой фреймворк, я вот peridot использую для этих целей).

Что, блин, значит «просто покрыть всё функциональными тестами»? Сделать тесты на отвали, а не нормальные тесты, которые действительно проверяют, что продукт делает то, что заявлено?

Для примера. Если тестировать пару REST API'шек, старую и новую. То в Codeception'е есть готовые функции «проверить что пришёл JSON», «проверить что пришёл JSON определённого формата». Применения таких функций достаточно для «просто покрыть всё функциональными тестами»? Мне, например, недостаточно. Я точно хочу видеть тест, который лезет в обе апихи с одинаковыми (по смыслу) запросами и сравнивает, что они дают одинаковые (по смыслу) ответы. С Behat'ом я легко могу словами человеческого языка написать

Если запрашиваю рецепт майского кекса в API v1
И запрашиваю рецепт пасхального кулича в API v2
То приходят одинаковые рецепты
И любому сразу понятно что я проверяю. Да, всё это можно сделать и Codeception'ом, но это никому кроме меня не будет понятно. И менеджеру тоже не будет понятно делаю я такую проверку или не делаю.

То есть Codeception хорош только для тривиальных тестов и неполного тестового покрытия? Где ж мякотка?

Camel ★★★★★ ()
Ответ на: комментарий от bitfroster

Буду краток

Буду краток: учи английский, пригодится.

Знаю, пригождается. Сказать-то что хотели? Что мне менеджеров/заказчиков надо учить английскому? Или вместе со знанием английского ко мне должна спуститься божественная благодать и понимание Codeception'а, который, кстати, дальше от английского чем Gherkin.

Camel ★★★★★ ()

Ещё суждение

https://5minphp.ru/episode10/

Codeception нахваливает человек, который сразу обозначает свою позицию:

Я, например, совершенно не сторонник такого подхода, что код должен быть обязательно тестируемый.

Camel ★★★★★ ()

Codeception действительно такое дерьмо каким кажется?

Нет, конечно. У тебя просто слишком завышенные ожидания. Манагерам некогда за тебя тесты писать и их выполнение мониторить, чёрный экран и мерцающие буковки в нём им на фиг не сдались и ничего полезного не говорит. А если бы говорили, наняли бы кодера по дешевле. :) Тесты пишешь для себя и коллег программистов.

anonymous ()
Ответ на: комментарий от anonymous

Понимание приходит с опытом

Тесты пишешь для себя и коллег программистов.

И? С ними тоже надо обсуждать что делать, а чего не делать. С Gherkin'ом это сразу понятно, потому что что написано, то и делается.

Манагерам некогда за тебя тесты писать и их выполнение мониторить

А мне с другими тестировщиками как быть? Когда пишем на Gherkin'е сразу всем всё понятно. На Codeception'е же чужие тесты как чужой код (собственно им и являющийся), надо разбирать.

Возвращаясь к манагерам. Если возникает вопрос о какой-то функциональности продукта, то можно уточнить у манагеров usage scenario, зафиксировать его в feature-файле и тыкать в разработчиков пока тест не позеленеет. С Codeception'ом так не получится, или не так красиво.

Camel ★★★★★ ()
Ответ на: Понимание приходит с опытом от Camel

А мне с другими тестировщиками как быть? Когда пишем на Gherkin'е сразу всем всё понятно. На Codeception'е же чужие тесты как чужой код (собственно им и являющийся), надо разбирать.

Если у тестировщиков возникает проблема с чтением кода Codeception из 2-го примера, у меня возникают сомнения в их проф. навыках.

И по поводу сабжа: да, он не идеален, но лучше пока ничего не придумали.

bitfroster ★★ ()
Ответ на: комментарий от bitfroster

Возникает проблема

Если у тестировщиков возникает проблема с чтением кода Codeception из 2-го примера, у меня возникают сомнения в их проф. навыках.

Вы неправильно меня поняли. Даже в тривиальном примере (одном из самых первых в документации) код на codeception'е перестаёт быть похожим на английский и превращается в PHP. Реальный пример вот (автор не я):

class S001_CreateCustomerAndAccountCest
{
    private $created_customers = [];
    private $test_config_data = null;

    function __construct() {
        $this->test_config_data = Fixtures::get('data');
    }

    public function _before(AcceptanceTester $I)
    {
        $I->haveHttpHeader('Content-Type', 'application/json');
    }

    public function _after(AcceptanceTester $I)
    {
    }

    public function CreateCustomerAndAccount(AcceptanceTester $I)
    {
        $I->wantTo('create customer and account for tests');

        $I->switchBaseUrl($this->test_config_data['backend_url']);
        $I->amHttpAuthenticated($this->test_config_data['backend_login'], $this->test_config_data['backend_pass']);

        $request_json = $this->test_config_data['json_rpc_request'];

//        $sip = $this->test_config_data['sip_id'][mt_rand(0, count($this->test_config_data['sip_id'])-1)];
//        $request_json = $this->test_config_data['json_rpc_request'];

        foreach($this->test_config_data['sip_id'] as $sip) {

//            $request_json['method'] = 'registerCustomerWithFullData';
//            $request_json['id'] = mt_rand(1, 65000);
//            $request_json['params'] = $this->test_config_data['customer_with_full_data_template'];
//            $request_json['params']['customer_data']['name'] .= $I->getRandomData();
//            $request_json['params']['customer_data']['login'] = sprintf($request_json['params']['customer_data']['login'], $request_json['params']['customer_data']['name']);
//            $request_json['params']['customer_data']['email'] = sprintf($request_json['params']['customer_data']['email'], $request_json['params']['customer_data']['name']);
//            $request_json['params']['customer_data']['bcc'] = $request_json['params']['customer_data']['email'];
//            $request_json['params']['customer_data']['country'] = 'ru';
//            $I->sendPOST(
//                '',
//                $request_json
//            );
//            $I->seeResponseIsJson();
//            $I->seeResponseCodeIs(200);
//            $resp = json_decode($I->grabResponse(), true);
//            $I->assertEquals(1, $resp['result']['success']);
            $this->created_customers[] = [
                'env' => $this->test_config_data['env'],
                'customer_name' => $sip,//$request_json['params']['customer_data']['name'],
                'customer_accounts' => null,
                'created' => time(),
                'in_use' => true
            ];
            $customer = end(array_values($this->created_customers));
            $I->saveCustomerData($customer);


//            $acc_name = $I->getRandomData();
//            $request_json['method'] = 'createAccount';
//            $request_json['id'] = mt_rand(1, 65000);
//            $request_json['params'] = $this->test_config_data['create_account_template'];
//            $request_json['params']['customer_name'] = $customer['customer_name'];
//            $request_json['params']['account_info']['sip_id'] = $sip;
//            $request_json['params']['account_info']['login'] = sprintf($request_json['params']['account_info']['login'], $acc_name);
//            $request_json['params']['account_info']['email'] = sprintf($request_json['params']['account_info']['email'], $acc_name);
//            $I->sendPOST(
//                '',
//                $request_json
//            );
//            $I->seeResponseIsJson();
//            $I->seeResponseCodeIs(200);
//            $resp = json_decode($I->grabResponse(), true);
//            $I->assertEquals(1, $resp['result']['success']);
            $customer['customer_accounts'][] = $sip;
            $query = sprintf($this->test_config_data['update_customer_data_query'], 'customer_accounts = :customer_accounts');
            Common::executeQuery(
                $query,
                [
                    'env' => $this->test_config_data['env'],
                    'customer_name' => $customer['customer_name'],
                    'customer_accounts' => $customer['customer_accounts'],
                ]
            );
        }
    }
Ну как, сразу всё поняли?

И по поводу сабжа: да, он не идеален, но лучше пока ничего не придумали.

Чем же Codeception хорош? По-моему он просто плох, а Behat хорош.

Camel ★★★★★ ()
Ответ на: Возникает проблема от Camel

Re: Возникает проблема

Да, сразу. Писавший код не осилил Git и оставил кучу ненужного хлама. :) Убрав комментарии ясно, что метод используется чисто для добавления записей:

public function CreateCustomerAndAccount(AcceptanceTester $I)
    {
        $I->wantTo('create customer and account for tests');

        $I->switchBaseUrl($this->test_config_data['backend_url']);
        $I->amHttpAuthenticated($this->test_config_data['backend_login'], $this->test_config_data['backend_pass']);

        $request_json = $this->test_config_data['json_rpc_request'];

        foreach($this->test_config_data['sip_id'] as $sip) {

            $this->created_customers[] = [
                'env' => $this->test_config_data['env'],
                'customer_name' => $sip,
                'customer_accounts' => null,
                'created' => time(),
                'in_use' => true
            ];
            $customer = end(array_values($this->created_customers));
            $I->saveCustomerData($customer);


            $customer['customer_accounts'][] = $sip;
            $query = sprintf($this->test_config_data['update_customer_data_query'], 'customer_accounts = :customer_accounts');
            Common::executeQuery(
                $query,
                [
                    'env' => $this->test_config_data['env'],
                    'customer_name' => $customer['customer_name'],
                    'customer_accounts' => $customer['customer_accounts'],
                ]
            );
        }
	}
anonymous ()
Ответ на: Re: Возникает проблема от anonymous

Обман

Вы пытаетесь обмануть меня и возможно себя.

        $request_json = $this->test_config_data['json_rpc_request'];

        foreach($this->test_config_data['sip_id'] as $sip) {

            $this->created_customers[] = [
                'env' => $this->test_config_data['env'],
                'customer_name' => $sip,
                'customer_accounts' => null,
                'created' => time(),
                'in_use' => true
            ];
            $customer = end(array_values($this->created_customers));
            $customer['customer_accounts'][] = $sip;
            $query = sprintf($this->test_config_data['update_customer_data_query'], 'customer_accounts = :customer_accounts');
            Common::executeQuery(
                $query,
                [
                    'env' => $this->test_config_data['env'],
                    'customer_name' => $customer['customer_name'],
                    'customer_accounts' => $customer['customer_accounts'],
                ]
            );
Вот это вот всё написано не на человеческом языке. Это нельзя просто прочитать, это можно понять только потратив некоторое количество усилий. Behat ставит пояснение того что происходит на первое место, поэтому сначала пишется gherkin-файл на языке похожем на человеческий. Я пока не вижу как Codeception мог бы сделать жизнь проще если в нём всё сделано прежде всего для машин, и только потом для людей.

Camel ★★★★★ ()
Ответ на: Обман от Camel

Re: Обман

Вот это вот всё написано не на человеческом языке.

Ну да, код некрасивый. Но Codeception тут вообще не причём, особенно в том участке кода, который ты вставил. :) Проблема в программисте, который это вот так написал. Behat ведь тоже целиком тест за программиста не напишет. Такой же код может быть в любом unit тесте.

anonymous ()
Ответ на: Обман от Camel

Re: Обман

Вот, написал чуть более читабельно. Сама идея программиста почему-то создавать account таким образом не очень хороша, но согласись, сейчас уже понять можно? :)

<?php

function createTestCustomersAndAccounts(AcceptanceTester $I) {

    $I->wantTo('create customer and account for tests');
    $I->switchBaseUrl($this->cfg['backend_url']);
    $I->amHttpAuthenticated($this->cfg['backend_login'], $this->cfg['backend_pass']);

    foreach ($this->cfg['sip_id'] as $sip) {
        $customer = $this->createTestCustomerData($sip);

        $I->saveCustomerData($customer);

        $this->testCustomers[] = $customer;
        $this->appendTestCustomerNameWithAccount($customer['customer_name'], $sip);
    }
}

function createTestCustomerData($sip) {
    return [
        'env' => $this->cfg['env'],
        'customer_name' => $sip,
        'customer_accounts' => null,
        'created' => time(),
        'in_use' => true
    ];
}

function appendTestCustomerNameWithAccount($customerName, $sip) {
    #That line is still terrible. :(
    $query = sprintf($this->cfg['update_customer_data_query'], 'customer_accounts = :customer_accounts');
    $params = [
        'env' => $this->cfg['env'],
        'customer_name' => $customerName,
        'customer_accounts' => [$sip],
    ];
    Common::executeQuery($query, $params);
}

anonymous ()
Ответ на: Re: Обман от anonymous

Провал

Ну вот сами посмотрите на демонстрируемый вами код. А теперь попробуйте сказать что это всё не так уж плохо Гради Бучу в лицо. Сложность надо прятать. Behat как раз позволяет это сделать. Behat позволяет писать понятные тесты на gherkin'е и скрывать сложность внутри определений step'ов.

Но таки может я всё же недопонял Codeception? Может у вас или кого-то ещё есть пример когда он пришёлся кстати? Вот прям идеально лёг и допиливать не потребовалось. Чтобы готовые функции codeception'а пришлись как раз. Потому что в тех случаях которые вижу я codeception без дополнительных функций не позволяет сделать нормального тестирования, а если уж пилить дополнительные функции, то лучше взять Behat и спрятать всю магию под капот, а снаружи оставить понятное объяснение. Взять даже мой пример с двумя API. Codeception, насколько я понял, не позволяет легко сделать такой тест, потому что в тесте предполагается, что actor работает с единственным API. В Behat'е такого искусственного ограничения нет.

Camel ★★★★★ ()
Ответ на: Провал от Camel

Re: Провал

Ты не понял примера - проблема была в прослойке между стулом и клавиатурой, не в Codeception. С такой же формулировкой можно выдвинуть заявку хоть Behat, хоть даже PHPUnit - это что за тестовый фрамеворк, если такую жуть приходится, как тут мне верно подсказывает, под капот прятать? :) Программист, который задумал так добавлять клиенты и ихние данные для acceptance тестов, это бы сделал в любом продукте.

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

anonymous ()
Ответ на: Re: Провал от anonymous

Уже лучше

Вот спасибо, анон.

Но таки ожидания у меня не завышены. Зачем терпеть Codeception если есть Behat?

Если в проекте, например, фронт на PHP, а бек на, скажем, Java, то логично взять cucumber, на худой конец Behat. Потому что тесты будут понятны всем. В какой ситуации Codeception засияет своим превосходством мне всё ещё неясно.

Camel ★★★★★ ()
Ответ на: Уже лучше от Camel

Re: Уже лучше

Codeception - то еще гавнище! в нем приходится пилить руками то, что уже давно реализовано в Selenium (если рассматривать ту часть которая отвечает за тестирование UI)

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.