Статьи

Обеспечить хороший возврат инвестиционного кода тестирования

Я всегда верил, что комплексная интеграция / функциональное тестирование кода гораздо важнее / продуктивнее, чем слепая попытка достичь 100% покрытия кода модульным тестированием. Я верю:
a) Хороший код, проверенный на интеграцию / функциональность, всегда обеспечит больший охват тестирования, чем код модульного тестирования.
б) Функциональный / интеграционный тестовый код позволяет нам фиксировать наши ошибки программирования как можно раньше и избегать ошибок интеграции «большого взрыва» в конце.

Таким образом, у меня всегда есть « здоровая и интересная » дискуссия с разработчиками, где они всегда верят:

  1. Достигнуть высокого процента охвата кода модульного тестирования очень важно.
  2. Каждый доставляемый класс должен быть «тестируемым модулем», все классы / ресурсы зависимостей должны быть «смоделированы», хотя иногда это очень сложно и требует много времени для установки таких фиктивных объектов.
  3. Если наш код не тестируемый, это должно быть что-то не так с нашим дизайном. Мы должны изменить наш код, чтобы сделать его тестируемым. Таким образом, сделать наш класс финальным, или наш класс не реализующий интерфейс, или использовать статические методы, или любые вещи, которые делают наш класс не « дружественным к юнит-тестированию », — это плохо.

Не поймите меня неправильно, я не на 100% против указанных выше пунктов, и я понимаю важность модульного тестирования. Однако я не буду впадать в крайности, затрачивая дополнительные усилия для достижения 100% покрытия кода модульного теста. Поэтому, как менеджер по разработке программного обеспечения, я уверен, и я призываю мою команду предоставить хороший код тестирования возврата инвестиций (ROI) . Поскольку Java-ресурсы обширны, и нам необходимо принимать во внимание непродуктивный код модульного тестирования, который мы учитываем.

 Итак, что считается хорошим кодом ROI:

  • Это должен быть тест «черного ящика», в котором разработчики передают определенные параметры методу, выполняют метод и проверяют возвращаемое значение по отношению к ожидаемому результату. Нас не должно волновать, как выполняется метод и какие основные ресурсы вызываются.
  • Код тестирования должен легко переходить в код функционального / интеграционного тестирования. Зачем изобретать велосипед?
  • Тестовый код должен разрабатываться вокруг ваших вариантов использования и обратно к функциям вашего приложения. Таким образом, используя приложение HR в качестве примера, для модульного тестирования ваша заявка на ежегодный отпуск всегда утверждается вашим менеджером, что дает хорошую стоимость ROI. Но для модульного тестирования простого метода получения или установки вашего объекта данных, или для проверки DAO при возврате объекта сотрудника, или для создания утилит для чтения файлов свойств или анализа поддельного XML-файла не получается хорошее значение ROI.

Чтобы объяснить это далее, предположим, что у меня есть класс бизнес-делегата с именем OrderManager, который зависит от InventoryManager, AccountManager и ShipmentManager. Существует метод, позволяющий проверить, обрабатывать заказ или нет, как показано ниже: 

Public class OrderManager {
     public boolean proceedOrder(OrderVO anOrder) {
             return ( inventoryManager.hasStock(anOrder) &&
                        accountManager.customerGoodCreadit(anOrder.getCustomer()) &&
                        shipmentManager.shipmentDateOK(anOder.getPreferShipmentDate()
                      );

}

Для модульного тестирования метода continueOrder () с помощью Mock:

  1. Сначала создайте макеты InventoryManager, AccountManager и ShipmentManager
  2. Проинструктируйте нашу фиктивную платформу о вызываемых имитационных методах и сообщите имитацию об ожидаемой последовательности вызова, и установите return для каждого фиктивного вызова.
  3. И, наконец, мы проверяем возвращаемое значение с нашим ожидаемым результатом и обеспечиваем вызов ложных методов.

Приведенный выше код дает нам хорошую рентабельность инвестиций? 

  • Это нарушает подход к тестированию «черного ящика», мы шаг за шагом рассказываем нашей фиктивной платформе о том, какие вызовы будут вызываться, и специально просим их вернуть значение, которое мы установили, а метод теста continueOrder () возвращает значение с ожидаемым результатом. , когда мы уже знаем, что он вернется из первых рук.
  • Что произойдет, если мы захотим изменить реализацию, например, изменить последовательность вызовов или ввести новую бизнес-логику, например, проверить, является ли клиент VIP-клиентом, который может иметь приоритет при получении своего предпочтительного отсека для отправки, нужно ли нам менять наш тестирующий код?
  • Даже если наш отчет о покрытии кодов единиц показывает, что мы покрываем этот метод на 100%, дает ли это нам какое-либо значение?

Например, хороший код тестирования ROI для приведенного выше варианта использования:
1. Установите количество продукта InventoryManager (скажем, продукта «A») на определенную сумму (скажем, 50).

2. Протестируйте метод continueOrder (), поместив продукт A в различные количества.
3. Сравните результат с ожидаемым результатом.

Итак, в заключение, я считаю, что если код модульного тестирования не приносит хорошей окупаемости, потратьте больше времени на предоставление кода функционального / интеграционного тестирования и обеспечение хорошего покрытия «ROI», пригодного для использования кода.

Примените мои замечания к автомобильной промышленности, мы не слышим об инженерах, которые создают фиктивные автомобильные шины или макет для дорожного покрытия для проверки тормозной системы автомобиля, он / она фактически «интегрирует» тормозную систему с набором шин ( сделано из разных каучуков), и проведите испытание на торможение на разных поверхностях (может быть, на роликовой доске с разными поверхностями), верно?

Делитесь своими взглядами на это.