EcomDev PHPUnit Tipp #13

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #13: EAV Fixtures beschleunigen

Nutzt man die EAV Fixtures, um Produkte, Kategorien und Kunden für Tests anzulegen, macht der Fixture Processor von EcomDev_PHPUnit vor jedem Test ein Backup der jeweils bestehenden Tabellen und spielt es anschließend wieder zurück. Da die “magento_unit_tests” Test-Datenbank standardmäßig als Kopie der aktuellen Magento-Datenbank angelegt wird, kann das sehr viel unnötiger Overhead sein.

Die EAV-Tabellen in der Test-Datenbank einmalig zu bereinigen, kann Tests, die sonst mehrere Minuten laufen um ein vielfaches beschleunigen. Dazu löschen wir in der Test-DB alle Datensätze in den Main Tables (die verknüpften Attribute etec. werden über Trigger automatisch gelöscht), mit Ausnahme der Standard Root Kategorie:

delete from catalog_product_entity;
delete from catalog_category_entity where entity_id > 2;
delete from customer_entity;

EcomDev PHPUnit Tipp #12

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #12: Cache

Man kann Caches pro Test an- und abschalten, mit den folgenden Annotations:

/**
 * @test
 * @cache on all
 */
public function testWithCache()

/**
 * @test
 * @cache off all
 */
public function testWithoutCache()

/**
 * @test
 * @cache off all
 * @cache on layout
 * @cache on block_html
 */
public function testWithSomeCachesTurnedOn()


/**
 * @test
 * @cache on all
 * @cache off translate
 * @cache off config
 */
public function testWithSomeCachesTurnedOff()

Die Annotations werden in der angegebenen Reihenfolge ausgeführt, wenn man also “all” benutzt, sollte es immer vor allen anderen @cache Annotations stehen.

EcomDev_PHPUnit Tipp #11

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #11: Minimal-Fixtures für Produkttypen

Es folgen einige Beispiel Fixtures für verschiedene Produkttypen. Sie sind so minimal gehalten, dass die Daten gerade ausreichen, um die Produkte und ggf. Produktbeziehungen anzulegen. In den meisten Fällen wird man zusätzliche Attribute wie Name, Status, Sichtbarkeit, Preis und Lagerbestand benötigen.

Continue reading “EcomDev_PHPUnit Tipp #11”

EcomDev_PHPUnit Tipp #10

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #10: Defekte Übersetzungen

Es kann passieren, dass Übersetzungen in Unit Tests nicht funktionieren, wenn man einen Helper mit einem Helper-Mock ersetzt (mit mockHelper() und replaceByMock()), insbesondere im Developer Mode, wo Übersetzungen nur funktionieren wenn sie für das richtige Modul definiert wurden.

Der Modulname wird in Mage_Core_Helper_Abstract mit $this->_getModuleName() ermittelt, also schauen wir uns die Methode einmal an:

/**
 * Retrieve helper module name
 *
 * @return string
 */
protected function _getModuleName()
{
    if (!$this->_moduleName) {
        $class = get_class($this);
        $this->_moduleName = substr($class, 0, strpos($class, '_Helper'));
    }
    return $this->_moduleName;
}

Wenn Deine Helper Klasse Your_Awesome_Helper_Data ist, wird die Klasse des Helper-Mocks Mock_Your_Awesome_Helper_Data heißen. Weil ein Modul namens Mock_Your_Awesome nicht existiert, gibt es keine Übersetzungen.

Lösung

Um Helper Unit-Test-sicher zu machen (und als Nebeneffekt auch Rewrite-sicher), definiere _moduleName explizit:

class Your_Awesome_Helper_Data extends Mage_Core_Helper_Abstract
{
    protected $_moduleName = 'Your_Awesome';
}

Zuerst beschrieben hier: http://magento.stackexchange.com/questions/46255/ecomdev-phpunit-translation-not-working-with-mocked-helper

Meine Checkliste bei der Analyse von Magento Shops

Der erste Schritt vor der Arbeit an einer bestehenden und mir unbekannten Magento Installation, ist bei mir immer eine Shop Analyse, nicht nur um die Anforderungen und Prozesse des Händlers besser zu verstehen, sondern auch und vor allen Dingen, um die Qualität einzuschätzen und mögliche Probleme frühzeitig zu erkennen. Denn leider sind viele Shops ohne Kenntnis von Konventionen und Best Practices “zusammengebastelt”, was dazu führt, dass:

  1. Die Performance leidet
  2. Sicherheitslücken entstehen
  3. Updates unmöglich werden
  4. Der Code nicht wartbar ist

Punkt 1 und 2 können den Händler ganz direkt schmerzhaft treffen, Punkt 3 und 4 führen mit der Zeit dazu, dass Bugs sich anhäufen, neue Änderungen häufig zu neuen Fehlern an anderer Stelle führen, die immer schwerer zu lösen sind.

Das ist dann häufig der Punkt, an dem eine neue Agentur oder ein neuer Entwickler gesucht wird, also ist es kein Zufall, dass Shops meist in solch degeneriertem Zustand auf meinem Tisch landen. Umso wichtiger ist die Analyse als erster Schritt, noch vor einem Angebot für Weiterentwicklungen, um nicht blind in die selbe Falle zu laufen wie die Vorgänger, und von Vorneherein für Klarheit auf beiden Seiten zu sorgen.

Continue reading “Meine Checkliste bei der Analyse von Magento Shops”

EcomDev_PHPUnit Tipp #9

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #9: Checkout Test

Vor 3 Jahren habe ich schonmal einen Artikel dazu geschrieben, wie man einen Integrationstest für den Checkout schreibt. Aber die Praktiken, die ich dort angewendet habe sind heute nicht mehr aktuell und einige der Workarounds sind nicht mehr notwendig. Dieser Beitrag zeigt, was notwendig ist um einen Test zu schreiben, der den Magento Checkout simuliert und nutzt dabei die in Tipp #1 gelernte Technik.

  1. Da einige Singletons involviert sind, stelle sicher, dass ihr Status zurückgesetzt wird:
    /*
     * @test
     * @singleton checkout/session
     * @singleton customer/session
     * @singleton checkout/cart
     */
    
  2. Es ist ratsam, als erstes den Warenkorb zu besuchen, um die Totals Collection zu triggern. Angenommen, der Kunde hat die ID 1 und einen aktiven Warenkorb (von zuvor im Test in den Warenkorb gelegten Produkten oder einer Quote Fixture), dann beginnen wir mit:
    $this->customerSession(1);
    $this->dispatch('checkout/cart');
    
  3. Vor jedem neuen Request, muss das Checkout Session Singleton manuell während des Tests zurückgesetzt werden, sonst wird die Quote nicht neu geladen und kann unter Umständen ganz verloren gehen:
    Mage::unregister('_singleton/checkout/session');
    $this->customerSession(1);
    $this->dispatch('checkout/onepage');
  4. Manchmal möchte man einen Kunden mit aktivem Warenkorb ausloggen. Dazu sind drei Schritte notwendig:
    Mage::getSingleton('customer/session')->logout();
    Mage::getSingleton('checkout/cart')->unsetData();
    $this->guestSession();

EcomDev_PHPUnit Tipp #8

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #8: “sort_order is ambiguous” Fehler

Zend_Db_Statement_Exception: SQLSTATE[23000]: Integrity constraint violation: 1052 Column ‘sort_order’ in order clause is ambiguous

Wer bei seinen Tests diesen Fehler von MySQL bekommt, hat wahrscheinlich bei EAV Fixtures das Attribut-Set vergessen. attribute_set_id sollte immer gesetzt sein, auch bei Kunden und Adressen (dort einfach “0” eintragen)

EcomDev_PHPUnit Tipp #7

Seit Jahren ist das Test-Framework EcomDev_PHPUnit quasi-Standard für Magento Unit Tests. Die aktuelle Version ist 0.3.7 und der letzte Stand der offiziellen Dokumentation ist Version 0.2.0 – seitdem hat sich viel getan, was man leider im Code und GitHub Issues selbst zusammensuchen muss. Diese Serie soll praktische Tipps zur Verwendung sammeln.

Tipp #7: YAML Verzeichnis-Fallback

YAML-Dateien für Fixtures, Expectations und Data Providers werden standardmäßig in folgendem Schema erwartet, wobei in diesem Beispiel Importer.php den Test Case enthält und testImport die Test-Methode ist:

│   Importer.php
│
├───Importer
│   ├───expectations
│   │       testImport.yaml
│   │
│   ├───fixtures
│   │       testImport.yaml
│   │
│   └───providers
│           testImport.yaml

Hier die Signatur mit Annotations:

    /**
     * @param string $csv CSV file content
     * @test
     * @loadFixture
     * @loadExpectation
     * @dataProvider dataProvider
     */
    public function testImport($csv)

Anstelle des Methodennamens kann der Dateiname der YAML-Datei jeweils explizit angegeben werden, hier z.B. in einem weiteren Test, der auch fixtures/testImport.yaml benutzt (wie das auch für Data Provider funktioniert, siehe Tipp #3).

    /**
     * @param string $csv CSV file content
     * @test
     * @loadFixture testImport
     * @dataProvider dataProvider
     * @expectedException RuntimeException
     */
    public function testFailingImport($csv)

Aber auch die Verzeichnisstruktur muss nicht in dieser Form genutzt werden. Findet EcomDev_PHPUnit die jeweilige Datei nämlich nicht an der Stelle, greift folgende Fallback-Hierarchie:

  1. Default: siehe oben
  2. Module: Die Verzeichnisse “fixtures”, “expectations” und “providers” werden direkt im “Test” Verzeichnis des Moduls gesucht
  3. Global: Die Verzeichnisse werden in sämtlichen Oberverzeichnissen des Test Cases bis hin zum Magento Root gesucht

Auf diese Weise lassen sich Fixtures etc. Test Case übergreifend verwenden. Ich bevorzuge mittlerweile zumindest für Fixtures in den meisten Fällen die zweite Variante für modulweite Definition.

Die Hierarchie ist übrigens in der config.xml von EcomDev_PHPUnit wie folgt definiert:

<config>
    <phpunit>
        <suite>
            <yaml>
                <model>ecomdev_phpunit/yaml_loader</model>
                <loaders>
                    <default>ecomdev_phpunit/yaml_loader_default</default>
                    <module>ecomdev_phpunit/yaml_loader_module</module>
                    <global>ecomdev_phpunit/yaml_loader_global</global>
                </loaders>
            </yaml>
        </suite>
    </phpunit>
</config>

Diese Loaders sind Models, die von EcomDev_PHPUnit_Model_Yaml_AbstractLoader erben, mit einem eigenen Modul lassen sich hier also theoretisch weitere, eigene Loader hinzufügen und somit beliebige Quellen für die YAML Dateien verwenden.