Модульное тестирование исходных моделей

10

У меня есть несколько моделей в моем собственном расширении, которые служат только для заполнения некоторых выборок и / или множественных выборок в форме добавления / редактирования моих сущностей.
Таким образом, они - то, что magento называет «исходными моделями».
Используемые значения всегда одинаковы, а методы возвращают одно и то же.
Как я должен тестировать их? Или даже лучше, я должен написать для них модульные тесты?
Вот пример.
Следующий класс используется для формы добавления / редактирования для вызываемого поля typeи для столбца сетки того же поля.

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}
Мариус
источник

Ответы:

15

Это отличная тема, и мне очень нравятся оба Ответа от @KAndy и @fschmengler.
Я хотел бы добавить некоторые дополнительные мысли, которые я считаю ценными, когда задаю вопрос, такой как «Должен ли я проверить X?» или «Как мне проверить X?».

Что может пойти не так?

  • Я мог бы сделать глупую опечатку (происходит постоянно).
    Это обычно не оправдывает написание теста.
  • Буду ли я копировать нужный мне код из ядра или другого модуля, а затем настраивать его под свои нужды?
    Я считаю это на самом деле очень опасным занятием, которое часто оставляет незаметные ошибки. В этом случае я предпочитаю написать тест, если он не слишком дорогой. Создание конфигурации исходных моделей на самом деле сделало бы их более рискованными ИМО.
  • Может ли быть конфликт с другим модулем?
    Это почти относится только к коду конфигурации. В таком случае мне нравится проводить интеграционный тест, который сообщает мне, когда это произойдет.
  • Может ли Magento изменить API в будущем выпуске?
    Очень маловероятно в этом случае, так как ваш код зависит только от интерфейса. Но если задействованы более конкретные классы или если мой код расширяет базовый класс, это становится более потенциальным риском.
  • Новая версия PHP может сломать мой код. Или, может быть, я хочу поддерживать PHP 5.6 на долгие годы.
    Опять же, это очень маловероятно, но в некоторых случаях я хочу, чтобы тест предупреждал меня, если в будущем я должен изменить код, чтобы использовать несовместимый синтаксис.

Насколько дорого стоит тестирование кода?

Это имеет два аспекта:

  • Количество усилий и времени, которое требуется, чтобы написать тест
  • Количество усилий и времени, необходимых для тестирования фрагмента кода, который я собираюсь написать вручную.

Во время разработки некоторого фрагмента кода мне, как правило, приходится запускать код, который я пишу, довольно часто, пока я не сочту это выполненным. Это, конечно, намного проще с модульным тестом.

В вашем случае написание теста очень дешево. Это не займет много времени или усилий. @Kandy прав, что нужно поддерживать весь код, но опять же, не все тесты должны быть сохранены.
Это может быть пример, где я бы написал модульный тест, просто чтобы убедиться, что я не делаю глупую ошибку, а затем удалил ее, когда класс закончил. Если тест не дает долгосрочной ценности, думаю, его удаление имеет смысл.

Этот вопрос также важен с точки зрения выбора типа теста для записи: например, единица измерения или интеграция.

Насколько ценен кусок кода, который я пишу?

Если фрагмент кода, который я пишу, важен для службы, предоставляемой модулем, я проверяю ее, независимо от того, насколько она тривиальна.
Если это всего лишь небольшой вспомогательный метод, например, ориентированный на пользовательский интерфейс, а не часть бизнес-логики, то, возможно, нет.

Нужно ли будет менять код?

Со временем я настолько привык иметь тестовое покрытие, что изменение открытого кода кажется очень небезопасным. Это включает в себя такие простые вещи, как добавление параметра в исходную модель, а также такие вещи, как перемещение класса в другую папку / пространство имен или переименование метода.
Наличие тестов для таких изменений неоценимо.

Нужна ли документация?

Насколько сложно использовать код? В вашем примере это тривиально, но в некоторых более сложных случаях наличие теста отлично подходит для документирования других разработчиков (или меня в течение нескольких месяцев).

Разведка и обучение

Если я работаю над некоторым кодом и не знаю, как его протестировать, я считаю очень ценным написать тест. Процесс почти всегда дает мне более глубокое понимание того, с чем я имею дело.
Это особенно актуально для разработчиков, которые все еще считают себя обучающимися тестированию.
Это также пример, в котором может иметь смысл удалить тест впоследствии, потому что основным значением, которое он предоставил, было обучение.

Дисциплина и стресс

Придерживаясь петли красно-зеленого-рефактора, я быстро иду. Это особенно верно под давлением. Таким образом, даже если какой-то фрагмент кода на самом деле не годится для тестирования, я все равно могу следовать TDD, особенно если этот код тривиален для тестирования.
Это держит меня в потоке и бдительность.

Что и как проверить?

Также учтите, что вы можете написать тест в очень разной степени детализации.

  • Проверка точного возвращаемого значения.
    Это будет очень жесткий тест, который нужно будет корректировать с учетом каждого изменения. Вы хотите, чтобы тест прервался, например, при изменении порядка элементов в возвращаемом массиве?
  • Тестирование структуры возвращаемого значения.
    Для исходной модели это может быть проверка каждого подмассива в виде двух записей: одна с ключом, labelа другая с valueключом.
  • Проверка класса реализует ArrayInterface.
  • Тестирование класса обеспечивает, getOptions()хотя этот метод не является частью реализованного интерфейса.

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

Резюме

Подводя итог: нет единственно верного ответа на вопрос, следует ли тестировать какой-то фрагмент кода или нет. Ответ будет отличаться для каждого разработчика в зависимости от обстоятельств.

Vinai
источник
2
Отличный ответ! Я хочу выделить часть «удалить тесты, когда они больше не дают ценности» - иногда старые тесты, которые помогли на начальном этапе разработки, просто мешают работе в долгосрочной перспективе.
Фабиан Шменглер
1
Вы только что ответили на 2 других вопроса, в которых я сомневался. спасибо
Мариус
6

На мой взгляд, нет общего ответа на «написать модульные тесты для исходных моделей, да или нет»

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

Для исходных моделей, которые являются не чем иным, как прославленными массивами (как в вашем примере), я бы не стал писать модульные тесты. Но каким-то образом вам нужно убедиться, что вы не ошиблись. Есть несколько вариантов:

  1. модульный тест
  2. интеграционный тест
  3. вручную посмотреть на конфигурацию

Вы следуете за TDD? Затем выберите между (1) и (2) или обоими. В противном случае выберите между (2) и (3).


Если вы используете исходные модели для параметров конфигурации системы, интеграционный тест (один тест для всех ваших параметров конфигурации) может отобразить раздел конфигурации и проверить, присутствуют ли <select>элементы и содержат ли они ожидаемые значения. Помните, что речь идет не о тестировании одного конкретного класса, а о том, что все правильно связано.


И, как сказал @KAndy, в идеале вам не нужно было бы столько шаблонов, просто расширьте базовый класс, который уже протестирован модулем, и переопределите свойство или определите значения во внешней конфигурации.

В этом сценарии модульные тесты для конкретных реализаций не дают никакой дополнительной ценности. Если у вас много таких классов, неплохо было бы написать базовый класс ArraySourceсамостоятельно, если только Magento не предоставляет его.


С таким базовым классом ваша исходная модель может выглядеть так:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}
Фабиан Шменглер
источник
Спасибо за подтверждение. Я попытаюсь превратить мои прославленные массивы в настраиваемый список опций, но применимо ли это к моделям, которые будут иметь ровно N опций? Как «исходная» модель источника.
Мариус
Я не вижу, как исходная модель «статус» будет отличаться от вашего примера «тип автора»
Фабиан Шменглер
потому что я мог бы использовать значения 0 и 1 (как константы класса) во внешнем интерфейсе, чтобы фильтровать, например, включенные объекты. Я имею в виду, что в этом случае значения параметров имеют логику. они не просто key=>valueпары.
Мариус
Но эта логика не является частью исходной модели, верно? Это означает, что здесь не важно. У вас все еще будут константы для использования в других местах. Я добавил пример того, как я использовал бы такое внедрение.
Фабиан Шменглер
5

Как я должен тестировать их?

Я думаю, что вы не должны.

Добавление кода в систему увеличивает стоимость поддержки и обслуживания, но процесс тестирования должен быть БЕЗУСЛОВНЫМ .

Более того, этот код не должен существовать. Я считаю, что в Magento должен быть один общий декларативный способ определения наборов опций для использования в разных местах. А твое нежелание писать тест для этого класса показывает мне запах плохого кода

Канди
источник
1
Спасибо. Таким образом, вы говорите, что эти параметры должны быть не жестко запрограммированы, а взяты из файла конфигурации (например, di.xml). Я могу это сделать. Но верно ли это для моделей источников статуса? Я имею в виду, если у меня тот же класс, что и выше, но только с включенным и отключенным состоянием (1,0), должен ли я использовать тот же подход конфигурации? Если да, я хочу сказать, что это кажется мне слишком сложным.
Мариус