Що є правильним способом тестування коду PHP7 з PHPUnit 4.1 у Magento 2?


23

Коли я пишу свої модулі, я намагаюся поставити їм одиничні тести для найважливіших частин програми. Однак на даний момент (Magento 2.1.3) існує кілька способів написання одиничних тестів:

Різні способи тестування

  • Інтегруйте його bin/magento dev:tests:run unitта запустіть його над типовими параметрами phpunit, що в комплекті з Magento.
  • Пишіть їх окремо, запускайте їх vendor/bin/phpunit app/code/Vendor/Module/Test/Unitі знущайтесь над усім, що є Magento.
  • Пишіть їх окремо, знущайтеся над усім і використовуйте глобальну системну версію PHPUnit.
  • Пишіть їх окремо, запускайте їх vendor/bin/phpunit, але все ж користуйтеся \Magento\Framework\TestFramework\Unit\Helper\ObjectManager.

Magento 2 і PHPUnit

Крім того, Magento 2 постачається в комплекті з PHPUnit 4.1.0, що не сумісно з PHP7. Уродженці типу натяку (типу stringта `int) та оголошення типу повернення у ваших підписах призведе до помилок. Наприклад, інтерфейс / клас із таким підписом методу:

public function foo(string $bar) : bool;

... не зможе знущатися PHPUnit 4.1.0. :-(

Моя нинішня ситуація

Через те, що зараз я переважно пишу свої одиничні тести третім способом (за допомогою виклику системно-глобальної версії PHPUnit).

У моїй установці у мене PHPUnit 5.6 встановлений у всьому світі, тож я можу вирішити написати належний PHP7-код, але мені доведеться зробити деякі налаштування. Наприклад:

phpunit.xml має виглядати так, щоб я міг скористатися композитором автозавантажувача:

<?xml version="1.0"?>
<phpunit bootstrap="../../../../../../vendor/autoload.php"
         colors="true">
    <testsuites>
        <testsuite name="Testsuite">
            <directory>.</directory>
        </testsuite>
    </testsuites>
</phpunit>

... і в усіх моїх setUp()-методах у мене є така перевірка, щоб я могла написати свої тести з сумісністю вперед:

// Only allow PHPUnit 5.x:
if (version_compare(\PHPUnit_Runner_Version::id(), '5', '<')) {
    $this->markTestSkipped();
}

Таким чином, коли мої тести виконуються вбудованим PHPUnit в Magentos, це не призводить до помилок.

Моє запитання

Тож ось моє запитання: це «здоровий» спосіб написання одиничних тестів? Тому що мені не здається правильним, що Magento поставляється в комплекті з цілою купою інструментів для тестування, і я не можу їх використовувати, оскільки я використовую PHP7. Я знаю, що на GitHub є квитки, які стосуються цього питання, але мені цікаво, як громада зараз пише тести.

Чи є спосіб написати одиничні тести в Magento 2, щоб мені не довелося «понижувати» код і все ще можу використовувати вбудовані помічники Magentos для знущання над усім, до чого торкається менеджер об’єктів? Або навіть погана практика використовувати диспетчер об'єктів навіть у ваших тестових підрозділах?

Мені не вистачає безлічі вказівок / прикладів щодо правильного способу тестування власних спеціальних модулів.


1
Яке чудове запитання.
camdixon

Відповіді:


17

Використання пакетної версії PHPUnit, навіть якщо вона давня, є, мабуть, найкращим способом, оскільки це дозволить запускати тести для всіх модулів разом під час CI.

Я думаю, що написання тестів у спосіб, несумісний із комплексною рамкою тестів, значно знижує значення тестів.
Звичайно, ви можете налаштувати CI для запуску своїх тестів з іншою версією PHPUnit, але це додає великої складності системі збирання.

Однак, я згоден з вами, що не варто підтримувати PHP 5.6. Я максимально використовую натяк на скалярний тип PHP7 та натяк на тип повернення (плюс мене не цікавить ринок).

Для того, щоб подолати обмеження глузуючої бібліотеки PHPUnit 4.1, є щонайменше два досить прямих обхідних шляхи, які я використовував у минулому:

  1. Наприклад, використовуйте анонімні або регулярні заняття для складання тестових пар

    $fooIsFalseStub = new class extends Foo implements BarInterface() {
        public function __construct(){};
        public function isSomethingTrue(string $something): bool
        {
            return false;
        }
    };
  2. Використовуйте пакетну PHPUnit, але третю сторону глузуючої бібліотеки, яку можна включити через композитор require-dev, наприклад https://github.com/padraic/mockery . Усі знущаються бібліотеки, які я намагався, можна дуже легко використовувати з будь-якою тестовою основою, навіть із дуже старою версією PHPUnit, як 4.1.

Жоден із них не має жодної технічної переваги перед іншими. Ви можете реалізувати будь-яку необхідну подвійну логіку тесту з будь-якою.

Особисто я вважаю за краще використовувати анонімні класи, оскільки це не додає до кількості зовнішніх залежностей, а також цікавіше писати їх таким чином.

EDIT :
Щоб відповісти на ваші запитання:

Чи «глузування» вирішує проблему PHPUnit 4.1.0 не в змозі належним чином обробити підказку типу PHP7?

Так, див. Приклад нижче.

І які переваги анонімних занять над глузуванням?

Використання анонімних класів для створення тестових пар - це також «глузування», воно насправді не відрізняється від використання глузуючої бібліотеки, наприклад PHPUnits або Mockery чи іншої.
Макет - це лише певний тип тестового двійника , незалежно від способу його створення.
Одна невелика різниця між використанням анонімних класів або глузуючої бібліотеки полягає в тому, що анонімні класи не мають зовнішньої бібліотечної залежності, оскільки це просто звичайний PHP. В іншому випадку переваг чи недоліків немає. Це просто питання переваги. Мені це подобається, тому що він ілюструє, що тестування не стосується будь-якої тестової рамки чи глузувальної бібліотеки, тестування - це лише написання коду, який виконує систему, що перевіряється, і автоматично перевіряє її роботу.

А як щодо оновлення версії PHPUnit в головному файлі composer.json до 5.3.5 (остання версія, що підтримує PHP7 і має публічні глузуючі методи (необхідні власні тести Magento 2))?

Це може бути проблематично, оскільки тести інших модулів та ядра тестуються лише за допомогою PHPUnit 4.1, і як такий ви можете зіткнутися з помилковими збоями в CI. Я думаю, що найкраще саме з цієї причини дотримуватися пакетної версії PHPUnit. @maksek сказав, що вони оновлюватимуть PHPUnit, але ETA для цього немає.


Приклад тесту з тестовим подвійним класом, для якого потрібен PHP7, який працює з PHPUnit 4.1, використовуючи бібліотеку Mockery:

<?php

declare(strict_types = 1);

namespace Example\Php7\Test\Unit;

// Foo is a class that will not work with the mocking library bundled with PHPUnit 4.1 
// The test below creates a mock of this class using mockery and uses it in a test run by PHPUnit 4.1
class Foo
{
    public function isSomethingTrue(string $baz): bool
    {
        return 'something' === $baz; 
    }
}

// This is another class that uses PHP7 scalar argument types and a return type.
// It is the system under test in the example test below.
class Bar
{
    private $foo;

    public function __construct(Foo $foo)
    {
        $this->foo = $foo;
    }

    public function useFooWith(string $s): bool
    {
        return $this->foo->isSomethingTrue($s);
    }
}

// This is an example test that runs with PHPUnit 4.1 and uses mockery to create a test double
// of a class that is only compatible with PHP7 and younger.
class MockWithReturnTypeTest extends \PHPUnit_Framework_TestCase
{
    protected function tearDown()
    {
        \Mockery::close();
    }

    public function testPHPUnitVersion()
    {
        // FYI to show this test runs with PHPUnit 4.1
        $this->assertSame('4.1.0', \PHPUnit_Runner_Version::id());
    }

    public function testPhpVersion()
    {
        // FYI this test runs with PHP7
        $this->assertSame('7.0.15', \PHP_VERSION);
    }

    // Some nonsensical example test using a mock that has methods with
    // scalar argument types and PHP7 return types.
    public function testBarUsesFoo()
    {
        $stubFoo = \Mockery::mock(Foo::class);
        $stubFoo->shouldReceive('isSomethingTrue')->with('faz')->andReturn(false);
        $this->assertFalse((new Bar($stubFoo))->useFooWith('faz'));
    }
}

Чи «глузування» вирішує проблему PHPUnit 4.1.0 не в змозі належним чином обробити підказку типу PHP7? І які переваги анонімних занять над глузуванням? А як щодо оновлення версії PHPUnit в головному composer.jsonфайлі до 5.3.5 (остання версія, що підтримує PHP7 і має публічні глузуючі методи (необхідні власними тестами Magento 2))? Стільки запитань зараз ...
Джіл Беркерс

Оновлений моя відповідь у відповідь на ваше запитання @GielBerkers
Vinai

Дякую за чудову відповідь. Зараз це абсолютно зрозуміло! Я думаю, я піду і спробую Сміятися. Анонімні заняття, здається, мені доведеться переосмислити багато, що вже пропонує Макетрі. Я спершу хотів вивчити основи PHPUnit і рухатись звідти. Я думаю, що зараз час є.
Giel Berkers

Чудово! Насолоджуйтесь вивченням насмішок, чудовою бібліотекою. Поки ви перебуваєте на цьому, можливо, перегляньте, зокрема, і бібліотеку тверджень - це буде встановлено автоматично з Mockery.
Вінай

3

Зараз Magento 2 підтримує наступні версії PHP:

"php": "~5.6.5|7.0.2|7.0.4|~7.0.6"

Це означає, що весь код, написаний командою Magento, працює на кожній підтримуваній версії.

Отже команда Magento не використовує лише функції PHP 7. Функції PHP 5.6 можуть бути охоплені PHPUnit 4.1.0.

Написавши власний код, ви можете робити все, що завгодно, і писати тести будь-яким способом. Але я вважаю, що ви не зможете публікувати своє розширення на Magento Marketplace через порушення вимог.


Насправді PHPUnit 5.7 підтримується на PHP 5.6, PHP 7.0 та PHP 7.1. PHPUnit 4.8 підтримувався на PHP 5.3 - 5.6. Отже, хоча Magento 2 підтримує PHP 5.6, він все одно може перейти на PHPUnit 5.7.
Vinai
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.