Автотесты в Magento 2, часть 1

29 окт. 2018 г.

В этой статье речь пойдет про автоматическое тестирование в CMS Magento 2. Использовалась Magento 2.2

Общеизвесно, что magento достаточно сложная система сама по себе. Она требовательна к ресурсам. Разрабатывать под нее сложно и долго. В целом, у меня сложилось очень неоднозначное впечатление от ее использования.

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

Имеет смысл писать unit тесты на автономный функционал. Например, есть метод, который что-то считает на основании входных параметров. Unit тест для этого идеально подходит. Если же у нас есть метод, который работает с базой данных либо моделями magento, то, скорее всего, unit тесты нам здесь не очень помогут. Т.к. по сути нам придется подменить все зависимости, которых может быть много. И в реальности мы ничего особо не проверим. Вполне возможно, то, что мы хотели проверить как раз находится в этих самых зависимостях.

В этом случае мы создаем тестовую базу данных и пишем интеграционные тесты. Которые будут проверять весь функцинал в сборе. В основном для Magento 2 как раз актуальны интеграционные тесты.

Настройка окружения для unit тестов

Итак, приступим. Для начала нам нужно настроить окружение. Общий служебный код, связанный с тестами находится в папке /dev/tests

Unit тесты

Сначала настроим окружение для unit тестов. Тут все довольно просто. Достаточно скопировать конфигурационный файл.

cd /dev/tests/unit 
cp phpunit.xml.dist phpunit.xml

У меня получился такой конфиг:

<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd"
         colors="true"
         beStrictAboutTestsThatDoNotTestAnything="false"
         bootstrap="./framework/bootstrap.php"
        >
    <testsuite name="Magento Unit Tests">
        <directory suffix="Test.php">../../../app/code/*/*/Test/Unit</directory>
        <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory>
        <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory>
        <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory>
        <directory suffix="Test.php">../../../vendor/*/module-*/Test/Unit</directory>
        <directory suffix="Test.php">../../../vendor/*/framework/Test/Unit</directory>
        <directory suffix="Test.php">../../../vendor/*/framework/*/Test/Unit</directory>
        <directory suffix="Test.php">./*/*/Test/Unit</directory>
    </testsuite>
    <php>
        <ini name="date.timezone" value="America/Los_Angeles"/>
        <ini name="xdebug.max_nesting_level" value="200"/>
    </php>
    <filter>
        <whitelist addUncoveredFilesFromWhiteList="true">
            <directory suffix=".php">../../../app/code/*</directory>
            <directory suffix=".php">../../../lib/internal/Magento</directory>
            <directory suffix=".php">../../../setup/src/*</directory>
            <exclude>
                <directory>../../../app/code/*/*/Test</directory>
                <directory>../../../lib/internal/*/*/Test</directory>
                <directory>../../../lib/internal/*/*/*/Test</directory>
                <directory>../../../setup/src/*/*/Test</directory>
            </exclude>
        </whitelist>
    </filter>
    <listeners>
        <listener class="Magento\Framework\TestFramework\Unit\Listener\ReplaceObjectManager"/>
    </listeners>
    <logging>
    </logging>
</phpunit>

Все, теперь можно запускать тесты. Все тесты можно запустить такой командой:

./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml

Правда тут есть проблема. Запускаются вообще все тесты, какие только возможно. В частности, стандартные тесты magento. Всего их около 20000. Тот факт, что команда разработки magneto пишет тесты не может не радовать. При этом все равно есть ощущение, что в системе достаточно багов. Из этих ~20000 тестов некоторые падают. Нет предела совершенству! Но главная проблема - работает все это довольно-таки долго. Мне ни разу не удалось дождаться, пока все тесты пройдут, даже на достаточно мощном железе. Вероятно, стандартные тесты имеет смысл запускать тем, кто разрабатывает саму Magento или конрибьютит в нее.

Нам же достаточно запустить тесты на собственный модуль. Это можно сделать такой командой:

./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml app/code/{Vendor}/{Module}/Test/Unit

Запускает только наши тесты. Все тесты зеленые. Работает относительно быстро. Хотя, опять же, есть к чему стремиться.

Интеграционные тесты

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

Настройка окружения достаточно хитрая. Описана в документации. У меня пролучилось настроить не сразу.

Сначала нужно скопировать конфигурационный файл:

cd /dev/tests/integration 
cp phpunit.xml.dist phpunit.xml

Затем создать базу данных для тестов

CREATE DATABASE magento_integration_tests CHARACTER SET utf8 COLLATE utf8_general_ci;

Залить дамп основной базы в тестовую базу. Этот шаг нигде особо не описан. И, возможно, так делать не совсем правильно. Но если этого не делать, то тесты не запускаются. Вместо этого идут ошибки с отсутствующими таблицами. https://magento.stackexchange.com/questions/199114/magento-2-integration-test-cant-run-store-website-doesnt-exist

mysqldump -uroot -p -hlocalhost magento_db > magento_db.sql
mysql -uroot -p -D magento_integration_tests < magento_db.sql

Дальше нужно настроить подключение к тестовой БД. Нужно скопировать и отредактировать файл /dev/tests/integration/etc/install-config-mysql.php.dist Возможно нужно убрать неиспользуемые параметры. В моем случае - настройки rabitmq.

cp /dev/tests/integration/etc/install-config-mysql.php.dist /dev/tests/integration/etc/install-config-mysql.php
nano /dev/tests/integration/etc/install-config-mysql.php

Пример конфига:

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

return [
    'db-host' => '127.0.0.1',
    'db-user' => 'root',
    'db-password' => '',
    'db-name' => 'magento_220_ee_integration_tests',
    'db-prefix' => 'prod_',
    'backend-frontname' => 'backend',
    'admin-user' => \Magento\TestFramework\Bootstrap::ADMIN_NAME,
    'admin-password' => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
    'admin-email' => \Magento\TestFramework\Bootstrap::ADMIN_EMAIL,
    'admin-firstname' => \Magento\TestFramework\Bootstrap::ADMIN_FIRSTNAME,
    'admin-lastname' => \Magento\TestFramework\Bootstrap::ADMIN_LASTNAME,

    //'amqp-host' => '127.0.0.1',
    //'amqp-port' => '5672',
    //'amqp-user' => 'guest',
    //'amqp-password' => 'guest',
];

Дальше мне пришлось отключить очистку базы.

nano /dev/tests/integration/phpunit.xml

Поменять параметр TESTS_CLEANUP

<const name="TESTS_CLEANUP" value="disabled"/>

Это тоже спорный момент. Возможно, это не лучший вариант. Однако без этого тесты у меня не запустились. Опять же, ругается на недостающие таблицы. Есть мнение, что очистка базы работает не совсем корректно. Необходимые таблицы просто не создаются. К тому же, наверняка, были бы проблемы с производительностью. Т.к база данных Magento достаточно тяжелая. И постоянная перезагрузка данных врядли будет быстро работать. Сейчас остается другая проблема. База общая для всех тестов. В результате можно нарваться на проблему, когда тесты влияют друг на друга. Частично можно решить с помощью фикстур и ручного обновления нужных данных перед тестами.

Теперь попробуем запустить тесты. Для этого перейдем в директорию с интерационными тестами.

cd dev/tests/integration

И запустим все тесты

../../../vendor/bin/phpunit

Скорее всего вы дождетесь выполнения нескольких тестов и на этом можно остановиться. Перейдем к следующему шагу. Запустим тесты для нашего модуля.

../../../vendor/bin/phpunit ../../../app/code/{Vendor}/{Module}/Test/Integration
../../../vendor/bin/phpunit ../../../app/code/{Vendor}/{Module}/Test/Integration/Model/OrderTest

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

../../../vendor/bin/phpunit ../../../app/code/{Vendor}/{Module}/Test/Integration/Model/OrderTest --filter {testLogin}

Ура! Тесты работают! Надеюсь, и у вас тоже... Дальше предлагаю рассмотреть процесс написания тестов для какого-нибудь модуля, кроме HelloWorld. Но там есть свои хитрости и это тема отдельной статьи. Еще раз напоминаю, что эта статья не является исчерпывающим руководством. Скорее это мой опыт написания тестов для Magento 2. Я допускаю, что-то здесь может быть не совсем правильно. В то же время, я думаю, что данный опыт поможет кому-то(например мне в будущем) сэкономить массу времени. На этом пока все. Спасибо за внимание!