Was ist der richtige Weg, um PHP7-Code mit PHPUnit 4.1 in Magento 2 zu testen?

23

Wenn ich meine Module schreibe, versuche ich, ihnen Komponententests für die kritischsten Teile der Anwendung zur Verfügung zu stellen. Es gibt jedoch im Moment (Magento 2.1.3) verschiedene Möglichkeiten, um Unit-Tests zu schreiben:

Verschiedene Testmethoden

  • Integrieren Sie es in bin/magento dev:tests:run unitund führen Sie es über die mit Magento mitgelieferten phpunit-Standardeinstellungen aus.
  • Schreiben Sie sie separat, führen Sie sie mit aus vendor/bin/phpunit app/code/Vendor/Module/Test/Unitund verspotten Sie alles, was Magento ist.
  • Schreiben Sie sie separat, verspotten Sie alles und verwenden Sie eine systemweite Version von PHPUnit.
  • Schreiben Sie sie separat, führen Sie sie mit aus vendor/bin/phpunit, aber nutzen Sie sie trotzdem \Magento\Framework\TestFramework\Unit\Helper\ObjectManager.

Magento 2 und PHPUnit

Außerdem wird Magento 2 mit PHPUnit 4.1.0 ausgeliefert, das nicht PHP7-kompatibel ist. Indem Sie Eingeborene (wie stringund `int) mit Typhinweisen versehen und Rückgabetypen in Ihren Signaturen deklarieren, werden Fehler ausgegeben. Zum Beispiel eine Schnittstelle / Klasse mit einer Methodensignatur wie folgt:

public function foo(string $bar) : bool;

... kann von PHPUnit 4.1.0 nicht verspottet werden. :-(

Meine aktuelle Situation

Aus diesem Grund schreibe ich meine Unit-Tests jetzt hauptsächlich auf die dritte Art und Weise (durch Aufrufen einer system-globalen PHPUnit-Version).

In meinem Setup habe ich PHPUnit 5.6 global installiert, damit ich den richtigen PHP7-Code schreiben kann, aber ich muss einige Verbesserungen vornehmen. Beispielsweise:

phpunit.xml muss so aussehen, damit ich den Composer-Autoloader verwenden kann:

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

... und in all meinen setUp()Methoden habe ich folgende Prüfung, damit ich meine Tests mit Aufwärtskompatibilität schreiben kann:

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

Auf diese Weise wird kein Fehler ausgegeben, wenn meine Tests von Magentos 'eingebautem PHPUnit ausgeführt werden.

Meine Frage

Hier ist meine Frage: Ist dies eine "gesunde" Art, Komponententests zu schreiben? Weil es mir nicht richtig erscheint, dass Magento mit einer ganzen Reihe von Tools zum Testen geliefert wird und ich sie nicht verwenden kann, weil ich PHP7 verwende. Ich weiß, dass es auf GitHub Tickets gibt, die dieses Problem beheben, aber ich frage mich, wie die Community derzeit ihre Tests schreibt.

Gibt es eine Möglichkeit, in Magento 2 Komponententests zu schreiben, damit ich meinen Code nicht 'downgraden' muss und trotzdem die integrierten Hilfsprogramme von Magentos verwenden kann, um alles zu verspotten, was der Objektmanager berührt? Oder ist es sogar eine schlechte Übung, den Objektmanager auch in Ihren Unit-Tests zu verwenden?

Ich vermisse viele Anleitungen / Beispiele, wie Sie Ihre eigenen benutzerdefinierten Module testen können.

Giel Berkers
quelle
1
Was für eine großartige Frage.
Camdixon

Antworten:

17

Die Verwendung der mitgelieferten PHPUnit-Version ist wahrscheinlich der beste Weg, auch wenn sie älter ist, da dann die Tests für alle Module zusammen während der CI ausgeführt werden können.

Ich denke, das Schreiben von Tests auf eine Weise, die mit dem gebündelten Testframework nicht kompatibel ist, verringert den Wert der Tests erheblich.
Natürlich können Sie CI so einrichten, dass Ihre Tests mit einer anderen Version von PHPUnit ausgeführt werden. Dies erhöht jedoch die Komplexität des Build-Systems.

Trotzdem stimme ich Ihnen zu, dass es sich nicht lohnt, PHP 5.6 zu unterstützen. Ich verwende so oft wie möglich PHP7-Skalartyphinweise und Rückgabetyphinweise (außerdem ist mir der Marktplatz egal).

Um die Einschränkungen der PHPUnit 4.1-Verspottungsbibliothek zu umgehen, gibt es mindestens zwei einfache Problemumgehungen, die ich in der Vergangenheit verwendet habe:

  1. Verwenden Sie zum Beispiel anonyme oder reguläre Klassen, um Ihre Test-Doubles zu erstellen

    $fooIsFalseStub = new class extends Foo implements BarInterface() {
        public function __construct(){};
        public function isSomethingTrue(string $something): bool
        {
            return false;
        }
    };
  2. Verwenden Sie die mitgelieferte PHPUnit, aber eine Spottbibliothek eines Drittanbieters, die über Composer eingebunden werden kann require-dev, beispielsweise mit https://github.com/padraic/mockery . Alle versuchten spottenden Bibliotheken können sehr einfach mit jedem Testframework verwendet werden, selbst mit einer sehr alten Version von PHPUnit wie 4.1.

Keines davon hat einen technischen Vorteil gegenüber dem anderen. Sie können jede erforderliche Testdoppellogik mit einem der beiden implementieren.

Persönlich bevorzuge ich die Verwendung anonymer Klassen, da dies nicht zur Anzahl externer Abhängigkeiten beiträgt und es auch mehr Spaß macht, sie auf diese Weise zu schreiben.

EDIT :
Um Ihre Fragen zu beantworten:

Löst Mockery das Problem, dass PHPUnit 4.1.0 nicht in der Lage ist, Hinweise auf PHP7-Typen korrekt zu verarbeiten?

Ja, siehe folgendes Beispiel.

Und was sind die Vorteile anonymer Klassen gegenüber dem Verspotten?

Die Verwendung anonymer Klassen zum Erstellen von Test-Doubles ist ebenfalls "spöttisch". Sie unterscheidet sich nicht wirklich von der Verwendung einer Spottbibliothek wie PHPUnits oder Mockery oder einer anderen.
Ein Mock bezieht sich nur auf einen bestimmten Testdoppeltyp , unabhängig davon, wie er erstellt wird.
Ein kleiner Unterschied zwischen der Verwendung anonymer Klassen oder einer spöttischen Bibliothek besteht darin, dass anonyme Klassen keine Abhängigkeit von externen Bibliotheken aufweisen, da es sich nur um einfaches PHP handelt. Ansonsten gibt es keine Vor- oder Nachteile. Es ist einfach eine Frage der Präferenz. Ich mag es, weil es zeigt, dass es beim Testen nicht um ein Testframework oder eine Spottbibliothek geht. Beim Testen wird nur Code geschrieben, der das zu testende System ausführt und dessen Funktion automatisch überprüft.

Und wie wäre es mit der Aktualisierung der PHPUnit-Version in der Hauptdatei composer.json auf 5.3.5 (die neueste Version, die PHP7 unterstützt und öffentliche Verspottungsmethoden hat (erforderlich für Magento 2-eigene Tests))?

Dies kann problematisch sein, da die Tests in anderen Modulen und im Core nur mit PHPUnit 4.1 getestet werden und Sie daher möglicherweise auf falsche Fehler in CI stoßen. Ich denke, es ist aus diesem Grund am besten, sich an die mitgelieferte Version von PHPUnit zu halten. @maksek sagte, sie werden PHPUnit aktualisieren, aber es gibt keine ETA dafür.


Beispiel für einen Test mit einem Test Double einer Klasse, für die PHP7 mit PHPUnit 4.1 unter Verwendung der Mockery-Bibliothek ausgeführt werden muss:

<?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'));
    }
}
Vinai
quelle
Löst Mockery das Problem, dass PHPUnit 4.1.0 nicht in der Lage ist, Hinweise auf PHP7-Typen korrekt zu verarbeiten? Und was sind die Vorteile anonymer Klassen gegenüber Spott? Und wie wäre es mit der Aktualisierung der PHPUnit-Version in der Hauptdatei composer.jsonauf 5.3.5 (die neueste Version, die PHP7 unterstützt, und öffentliche Verspottungsmethoden (erforderlich für Magento 2-eigene Tests))? So viele weitere Fragen jetzt ...
Giel Berkers
Meine Antwort auf Ihre Frage wurde aktualisiert. @GielBerkers
Vinai
Vielen Dank für Ihre tolle Antwort. Es ist jetzt völlig klar! Ich denke, ich werde es dann mit Mockery versuchen. Anonyme Klassen scheinen, als müsste ich eine Menge neu erfinden, die Mockery bereits bietet. Ich wollte zuerst die Grundlagen von PHPUnit lernen und von dort aus weitermachen. Ich denke, jetzt ist die Zeit da.
Giel Berkers
Groß! Viel Spaß beim Erkunden von Mockery, einer großartigen Bibliothek. Wenn Sie schon dabei sind, sehen Sie sich vielleicht auch eine Assertionsbibliothek von hamcrest an - diese wird automatisch mit Mockery installiert.
Vinai
3

Momentan unterstützt Magento 2 folgende PHP-Versionen:

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

Dies bedeutet, dass der gesamte von Magento Team geschriebene Code auf jeder unterstützten Version funktioniert.

Daher verwendet Magento Team nicht nur PHP 7-Funktionen. PHP 5.6-Funktionen können mit PHPUnit 4.1.0 abgedeckt werden.

Wenn Sie Ihren eigenen Code schreiben, können Sie alles tun, was Sie wollen, und Tests schreiben, wie Sie möchten. Ich bin jedoch der Meinung, dass Sie Ihre Erweiterung aufgrund von Verstößen gegen die Anforderungen nicht auf Magento Marketplace veröffentlichen können.

yaronish
quelle
Tatsächlich wird PHPUnit 5.7 unter PHP 5.6, PHP 7.0 und PHP 7.1 unterstützt. PHPUnit 4.8 wurde von PHP 5.3 - 5.6 unterstützt. Obwohl Magento 2 PHP 5.6 unterstützt, kann es dennoch auf PHPUnit 5.7 aktualisiert werden.
Vinai