Блок тестування внутрішніх компонентів


14

Наскільки ви перевіряєте внутрішні / приватні компоненти класу / модуля / пакета / тощо? Ви їх взагалі тестуєте чи просто ви перевіряєте інтерфейс із зовнішнім світом? Прикладом таких внутрішніх є приватні методи.

Як приклад, уявіть рекурсивний аналізатор спуску , який має кілька внутрішніх процедур (функцій / методів), викликаних однією центральною процедурою. Єдиним інтерфейсом до зовнішнього світу є центральна процедура, яка займає рядок і повертає проаналізовану інформацію. Інші процедури розбирають різні частини рядка, і вони викликаються або з центральної процедури, або з інших процедур.

Природно, ви повинні протестувати зовнішній інтерфейс, викликаючи його зразками рядків і порівнюючи його з розібраним вручну результатом. А як щодо інших процедур? Ви перевірите їх окремо, щоб перевірити, чи правильно вони аналізують їхні підрядки?

Я можу придумати кілька аргументів:

Плюси :

  1. Більше тестування завжди краще, і це може допомогти збільшити охоплення коду
  2. Деяким внутрішнім компонентам може бути важко дати конкретні входи (наприклад, крайні регістри), надавши вхід до зовнішнього інтерфейсу
  3. Чітке тестування. Якщо внутрішній компонент має (фіксовану) помилку, тестовий випадок для цього компонента дає зрозуміти, що помилка була у цьому конкретному компоненті

Мінуси :

  1. Рефакторинг стає занадто болючим і трудомістким. Щоб щось змінити, вам потрібно переписати одиничні тести, навіть якщо на користувачів зовнішнього інтерфейсу це не впливає
  2. Деякі мови та тестуючі рамки цього не дозволяють

Яка ваша думка?


можливий дублікат або значне перекриття з: programmers.stackexchange.com/questions/10832/…
azheglov

Відповіді:


8

Випадок: "модуль" (в широкому сенсі, тобто щось, що має публічний інтерфейс, а можливо, і деякі приватні внутрішні частини) має в собі деяку складну / пов'язану логіку. Тестування лише інтерфейсу модуля буде свого роду інтеграційним тестуванням щодо внутрішньої структури модуля, і, таким чином, у випадку виявлення помилки таке тестування не буде локалізувати точну внутрішню частину / компонент, що відповідає за вихід з ладу.

Рішення: перетворіть складні внутрішні частини в самі модулі, протестуйте їх (і повторіть ці кроки для них, якщо вони занадто складні самі) та імпортуйте у свій оригінальний модуль. Тепер у вас є лише набір модулів, досить простий для унітаз-тестування (обидва перевіряють правильність поведінки та виправляють помилки) легко, і це все.

Примітка:

  • не буде необхідності нічого змінювати в тестах модулів (колишніх) "підмодулів" при зміні контракту модуля, якщо "підмодуль" більше не пропонує послуги, достатні для виконання нового / зміненого контракту.

  • нічого не буде оприлюднено, тобто договір модуля буде зберігатися та зберігатись в капсулі.

[Оновлення]

Щоб перевірити деяку розумну внутрішню логіку в тих випадках, коли важко перевести внутрішні частини об'єкта (я маю на увазі членів, які не є приватними імпортованими модулями / пакетами), у відповідний стан, лише подаючи його на вхід через публічний інтерфейс об'єкта:

  • просто мати якийсь код тестування з доступом до товаришів (в термінах C ++) або пакету (Java), які фактично встановлюють стан зсередини і перевіряють поведінку як слід.

    • це знову не порушить інкапсуляцію, забезпечивши простий прямий доступ до внутрішніх служб для тестових цілей - просто запустіть тести як "чорну скриньку" та компілюйте їх у версії версій.

і макет списку здається трохи порушеним; (
mlvljr

1
Гарна відповідь. У .NET ви можете використовувати [assembly: InternalsVisibleTo("MyUnitTestAssembly")]атрибут у вашому AssemblyInfo.csдля тестування внутрішніх даних. Хоча це схоже на обман.
Ніхто

@rmx Колись щось відповідає всім необхідним критеріям, це не є обманом, навіть якщо має щось спільне з фактичними кодами. Але тема між- / внутрішньомодульного доступу насправді дещо налаштована у сучасних основних мовах.
mlvljr

2

Підхід до коду, заснованого на FSM, трохи відрізняється від використовуваного традиційно. Це дуже схоже на те, що описано тут для апаратного тестування (яке, як правило, також FSM).

Коротше кажучи, ви створюєте тестову вхідну послідовність (або набір тестових вхідних послідовностей), яка повинна створювати не тільки певний вихід, але й при створенні певного "поганого" виводу дозволяє ідентифікувати невдалий компонент за характером відмови. Підхід досить масштабований, чим більше часу ви витратите на тестову конструкцію, тим краще буде тест.

Цей вид тестування ближче до того, що називається "функціональні тести", але воно виключає необхідність змінювати тести щоразу, коли ви трохи торкаєтесь програми.


2

Ну - це залежить :-). Якщо ви дотримуєтеся BDD (поведінки, керованої розробкою) або ATDD (Acceptance Test Driven Development), то тестування публічного інтерфейсу є прекрасним (до тих пір, поки ви його вичерпно протестуєте з різними входами. Основна реалізація, наприклад, приватні методи не є насправді важливо.

Однак скажіть, що ви хочете, щоб частина цього алгоритму виконувалася протягом певного часового проміжку або вздовж певної кривої bigO (наприклад, nlogn), то так, тестування окремих деталей має значення. Дехто назвав би це традиційним підходом TDD / Unit Test.

Як і все, YMMV


1

Розділити його на кілька частин з функціональним значенням, наприклад ParseQuotedString(), ParseExpression(), ParseStatement(), ParseFile()і зробити їх усіх громадськості. Наскільки ймовірно, що ваш синтаксис зміниться настільки, що вони стають неактуальними?


1
такий підхід легко призводить до слабшої інкапсуляції та більшого та складного у використанні / розумінні інтерфейсів.
сара
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.