Qual è il modo corretto per testare l'unità PHP7 con PHPUnit 4.1 in Magento 2?


23

Quando scrivo i miei moduli, sto cercando di fornire loro test unitari per le parti più critiche dell'applicazione. Tuttavia, al momento (Magento 2.1.3) esistono diversi modi su come scrivere unit test:

Diversi modi di test

  • Integralo con bin/magento dev:tests:run united eseguilo sopra le impostazioni predefinite di phpunit in bundle con Magento.
  • Scrivili separatamente, vendor/bin/phpunit app/code/Vendor/Module/Test/Uniteseguili e prendi in giro tutto ciò che è Magento.
  • Scrivili separatamente, prendi in giro tutto e usa una versione globale di PHPUnit.
  • Scrivili separatamente, eseguili con vendor/bin/phpunit, ma comunque fai uso di \Magento\Framework\TestFramework\Unit\Helper\ObjectManager.

Magento 2 e PHPUnit

Oltre a ciò, Magento 2 viene fornito in bundle con PHPUnit 4.1.0, che non è compatibile con PHP7. I nativi che suggeriscono il tipo (like stringe `int) e che dichiarano i tipi restituiti nelle firme genereranno errori. Ad esempio, un'interfaccia / classe con una firma del metodo come questa:

public function foo(string $bar) : bool;

... non potrà essere deriso da PHPUnit 4.1.0. :-(

La mia situazione attuale

È per questo motivo che ora sto scrivendo principalmente i miei test unitari nel terzo modo (chiamando una versione PHPUnit globale del sistema).

Nella mia configurazione, ho installato PHPUnit 5.6 a livello globale, quindi posso risolvere scrivere il codice PHP7 corretto, ma devo fare alcune modifiche. Per esempio:

phpunit.xml deve apparire così in modo da poter usare il caricatore automatico del compositore:

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

... e in tutti i miei setUp()metodi, ho il seguente controllo in modo da poter scrivere i miei test con compatibilità diretta:

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

In questo modo, quando i miei test vengono eseguiti dal PHPUnit integrato di Magentos, non genera un errore.

La mia domanda

Quindi, ecco la mia domanda: è un modo "salutare" di scrivere test unitari? Perché non mi sembra giusto che Magento sia fornito con un sacco di strumenti per aiutare con i test e non posso usarli perché sto usando PHP7. So che ci sono biglietti su GitHub che risolvono questo problema, ma mi chiedo come la community stia attualmente scrivendo i suoi test.

Esiste un modo per scrivere unit test in Magento 2, quindi non devo "downgrade" del mio codice e posso ancora usare gli helper integrati di Magentos per deridere tutto ciò che tocca il gestore degli oggetti? O è addirittura una cattiva pratica usare il gestore oggetti anche nei test delle unità?

Mi mancano molte indicazioni / esempi su quale sia il modo corretto su come testare i propri moduli personalizzati.


1
Che bella domanda.
Camdixon,

Risposte:


17

L'utilizzo della versione in bundle di PHPUnit, anche se è antica, è probabilmente il modo migliore di procedere poiché consentirà di eseguire i test per tutti i moduli durante CI.

Penso che scrivere i test in modo incompatibile con il framework di test in bundle riduca notevolmente il valore dei test.
Ovviamente è possibile impostare CI per eseguire i test con una versione diversa di PHPUnit, ma ciò aggiunge molta complessità al sistema di compilazione.

Detto questo, sono d'accordo con te sul fatto che non valga la pena sostenere PHP 5.6. Uso il suggerimento del tipo scalare PHP7 e il suggerimento del tipo restituito il più possibile (inoltre, non mi interessa il mercato).

Per aggirare i limiti della libreria di derisione di PHPUnit 4.1 ci sono almeno due soluzioni piuttosto semplici che ho usato in passato:

  1. Utilizza classi anonime o regolari per creare i tuoi doppi di test, ad esempio

    $fooIsFalseStub = new class extends Foo implements BarInterface() {
        public function __construct(){};
        public function isSomethingTrue(string $something): bool
        {
            return false;
        }
    };
  2. Usa la PHPUnit in bundle ma una libreria di derisione di terze parti che può essere inclusa tramite il compositore require-dev, ad esempio https://github.com/padraic/mockery . Tutte le librerie beffarde che ho provato possono essere facilmente utilizzate con qualsiasi framework di test, anche con una versione molto vecchia di PHPUnit come la 4.1.

Nessuno di questi ha alcun vantaggio tecnico rispetto all'altro. È possibile implementare qualsiasi logica di test doppia richiesta con entrambi.

Personalmente preferisco usare classi anonime perché ciò non aumenta il numero di dipendenze esterne, ed è anche più divertente scriverle in quel modo.

EDIT :
per rispondere alle tue domande:

La beffa 'risolve' il problema di PHPUnit 4.1.0 non essere in grado di gestire correttamente i suggerimenti di tipo PHP7?

Sì, vedi l'esempio seguente.

E quali sono i vantaggi delle lezioni anonime rispetto al deridere?

Anche l'uso di classi anonime per creare doppi di test è "beffardo", non è molto diverso dall'uso di una libreria beffarda come PHPUnits o Mockery o altro.
Un finto è solo su un tipo specifico di test doppio , indipendentemente da come viene creato.
Una piccola differenza tra l'uso di classi anonime o una libreria beffarda è che le classi anonime non hanno una dipendenza da libreria esterna, poiché è semplicemente PHP. Altrimenti non ci sono vantaggi o svantaggi. È semplicemente una questione di preferenza. Mi piace perché dimostra che i test non riguardano alcun framework di test o librerie beffardo, il test consiste semplicemente nello scrivere codice che esegue il sistema in prova e lo convalida automaticamente.

E che dire dell'aggiornamento della versione di PHPUnit nel file principale composer.json alla 5.3.5 (l'ultima versione che supporta PHP7 e con metodi di derisione pubblici (richiesti dai test di Magento 2))?

Ciò potrebbe essere problematico poiché i test in altri moduli e nel core sono testati solo con PHPUnit 4.1 e, pertanto, potresti riscontrare falsi errori in CI. Penso che sia meglio attenersi alla versione in bundle di PHPUnit per questo motivo. @maksek ha detto che aggiorneranno PHPUnit, ma non esiste ETA per questo.


Esempio di un test con un doppio di test di una classe che richiede PHP7 in esecuzione con PHPUnit 4.1, utilizzando la libreria 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'));
    }
}

La beffa 'risolve' il problema di PHPUnit 4.1.0 che non è in grado di gestire correttamente i suggerimenti di tipo PHP7? E quali sono i vantaggi delle lezioni anonime rispetto al deridere? E che dire dell'aggiornamento della versione di PHPUnit nel composer.jsonfile principale alla 5.3.5 (l'ultima versione che supporta PHP7 e con metodi di derisione pubblici (richiesti dai test di Magento 2))? Tante altre domande ora ...
Giel Berkers,

Aggiornato la mia risposta in risposta alla tua domanda @GielBerkers
Vinai

Grazie per l'ottima risposta. Adesso è completamente chiaro! Penso che andrò a provare Mockery allora. Sembra che dovrei reinventare molto le lezioni anonime che Mockery offre già. Prima volevo imparare le basi di PHPUnit e passare da lì. Penso che ora sia il momento.
Giel Berkers,

Grande! Divertiti ad esplorare Mockery, una grande biblioteca. Mentre ci sei, forse dai un'occhiata anche a hamcrest, una libreria di asserzioni - verrà installata automaticamente con Mockery.
Vinai,

3

Al momento Magento 2 supporta le prossime versioni di PHP:

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

Significa che tutto il codice scritto dal Team Magento funziona su ogni versione supportata.

Quindi Magento Team non utilizza solo le funzionalità di PHP 7. Le funzionalità di PHP 5.6 possono essere coperte con PHPUnit 4.1.0.

Scrivendo il tuo codice puoi fare tutto ciò che vuoi e scrivere test nel modo che preferisci. Ma credo che non sarai in grado di pubblicare la tua estensione sul mercato Magento a causa della violazione dei requisiti.


In realtà, PHPUnit 5.7 è supportato su PHP 5.6, PHP 7.0 e PHP 7.1. PHPUnit 4.8 è stato supportato su PHP 5.3 - 5.6. Quindi, anche se Magento 2 supporta PHP 5.6, potrebbe comunque eseguire l'aggiornamento a PHPUnit 5.7.
Vinai,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.