Чи варто введення залежності залежно від UnitTesting


17

Враховуючи конструктор, який ніколи і ніколи не повинен буде використовувати будь-які різні реалізації декількох об'єктів, які він ініціалізує, чи все-таки практичним є використання DI? Зрештою, ми можемо все ж захотіти провести тест.

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



3
KISS - це завжди найкращий підхід.
Реакційний

5
На мою думку, це питання є більш конкретним, ніж запропонований дублікат. дизайн-на-майбутнє зміни-або-вирішення-проблеми-під рукою , тому я голосую, щоб не закривати це
k3b

1
Чи не достатньо підстав для доказування?
ConditionRacer

Це насправді не звучить як дублікат настільки, як відповідати на себе. Якщо це ніколи не відбудеться, проектування для нього не буде проектуванням для майбутніх змін. Навіть космонавти архітектури розробляють лише зміни, які ніколи не відбудуться з поганого судження.
Стів314

Відповіді:


13

Це залежить від того, наскільки ви впевнені, що "ніколи і ніколи" правильно (під час використання вашої програми).

В загальному:

  • Не змінюйте дизайн лише для перевірки. Одиничні тести є для того, щоб служити вам, а не навпаки.
  • Не повторюйте себе. Створювати інтерфейс, який буде мати колись одного спадкоємця, марно.
  • Якщо клас не перевіряється, він, ймовірно, не є гнучким / розширюваним і для реальних випадків використання. Іноді це нормально - вам навряд чи потрібна ця гнучкість, але простота / надійність / ремонтопридатність / продуктивність / що важливіше.
  • Якщо ви не знаєте, введіть інтерфейс. Вимоги змінюються.

12
Майже дав вам -1 за те, що "не змінюйте дизайн лише для перевірки". Щоб отримати перевірку, IMHO - одна з головних причин зміни дизайну! Але ваші інші моменти в порядку.
Doc Brown

5
@docBrown - (та інші) я дійсно відрізняюся від багатьох, а може, навіть і від більшості. ~ 95% часу, що робить речі перевіреними, також робить їх гнучкими або розтяжними. Ви повинні мати це у своєму дизайні (або додати це до свого дизайну) більшість часу - проклятість проклята. Інший ~ 5% або має незвичайні вимоги, що перешкоджають такому гнучкості на користь чогось іншого, або настільки важливий / незмінний, що ви дізнаєтесь досить швидко, якщо він порушений, навіть без спеціальних тестів.
Теластин

4
може бути впевненим, але ваше перше твердження вище може бути витлумачено занадто легко як "залишити погано розроблений код, як це, коли ваша єдина проблема - перевірка".
Doc Brown

1
Чому б ви сказали «Створення інтерфейсу, який коли-небудь матиме одного спадкоємця, марний»? Інтерфейс може працювати як договір.
каптан

2
@kaptan - адже конкретний об’єкт може працювати так само добре, як і контракт. Зробити два просто означає, що вам потрібно написати код двічі (і змінити код двічі, коли він змінюється). Зрозуміло, переважна більшість інтерфейсів матиме декілька (нетестових) реалізацій або вам потрібно буде підготуватися до такої можливості. Але для того рідкісного випадку, коли все, що вам потрібно, - це простий герметичний предмет, просто використовуйте його безпосередньо.
Теластин

12

Чи виправдано ми уникати спроб програмування до інтерфейсу?

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

Наступне питання: чи є частиною відповідальності відповідного класу за вибір реалізації чи ні? Це може бути, а може і не бути, і ви повинні діяти відповідно.

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

YAGNI припускають, що при написанні коду важливо не додавати нічого зайвого. Однією з речей, яку програмісти, як правило, додають до свого коду, навіть не усвідомлюючи цього, - зайві припущення. Як "цей метод може стати в нагоді" або "це ніколи не зміниться". Обидва додають безладу вашому дизайну.


2
+1 для того, щоб цитувати YAGNI тут як аргумент для DI, а не проти.
Doc Brown

4
@DocBrown: Враховуючи, що YAGNI можна використовувати тут, щоб також виправдати не використовувати DI. Я думаю, що це більше аргумент проти того, щоб YAGNI був корисним заходом для будь-якого.
Стівен Еверс

1
+1 за те, що зайві припущення включаються до ЯГНІ, які кілька разів ударили нас по голові
Ізката

@SteveEvers: Я думаю, що використання YAGNI для виправдання ігнорування принципу інверсії залежності виключає нерозуміння YAGNI або DIP або, можливо, навіть деякі більш фундаментальні принципи програмування та міркування. Ви повинні ігнорувати DIP лише в разі потреби - обставини, при якій YAGNI просто не застосовується за своєю суттю.
back2dos

@ back2dos: припущення, що DI корисний через "можливо-вимоги" та "хто знає?" саме течія думок, що YAGNI призначена для боротьби, замість цього ви використовуєте це як виправдання для додаткового інструментарію та інфраструктури.
Стівен Еверс

7

Це залежить від різних параметрів, але ось декілька причин, чому ви все-таки можете використовувати DI:

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

Підсумок: ви, ймовірно, можете жити без DI в такому випадку, але є ймовірність, що використання DI перетвориться на чистіший код.


3

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

Додаткові переваги Dependency Injection та використання інтерфейсів замість класів також вигідні, коли додаток розширено, але вони також можуть бути модернізовані до вихідного коду пізніше досить легко та безпечно, використовуючи пошук та заміну. - навіть на дуже великих проектах.


2
 > Dependency Injection worth it **outside** of UnitTesting?
 > Are we justified in avoiding trying to program to an interface?

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

Якщо ви запитуєте, які причини, крім одиничних тестів, вимагають запрограмувати інтерфейс

Так , введення залежностей варто цього поза UnitTesting, якщо вам потрібна інверсія управління . Наприклад, якщо для реалізації одного модуля потрібен інший модуль, недоступний у своєму шарі.

Приклад: якщо у вас є модулі gui=> businesslayer=> driver=> common.

У цьому сценарії businesslayerможна використовувати, driverале driverне може використовувати businesslayer.

Якщо driverпотрібен певний функціонал вищого рівня, ви можете реалізувати цю функціональність, в businesslayerякій реалізується інтерфейс у commonшарі. driverпотрібно знати лише інтерфейс у commonшарі.


1

Так, ви - по-перше, забудьте про тестування одиниць, як про причину проектування коду навколо інструментів тестування одиниць, ніколи не годиться згинати дизайн коду для штучного обмеження. Якщо ваші інструменти змушують вас це робити, отримайте кращі інструменти (наприклад, Microsoft Fakes / Moles, які дозволяють вам набагато більше варіантів для створення макетних об'єктів).

Наприклад, чи розділили б ви свої класи лише на загальнодоступні методи лише тому, що інструменти тестування не працюють з приватними методами? (Я знаю, що переважаюча мудрість полягає в тому, щоб робити вигляд, що вам не потрібно перевіряти приватні методи, але я вважаю, що це реакція на труднощі при використанні поточних інструментів, а не справжня реакція на те, що не потрібно тестувати приватних осіб).,

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


3
Я не думаю, що переважаюча думка про те, що приватні методи не слід перевіряти, має нічого спільного з труднощами їх тестування. Якщо в приватному методі була помилка, але вона не впливала на поведінку об'єкта, то це нічого не впливає. Якщо в приватному методі виникла помилка, яка вплинула на поведінку об'єкта, то принаймні один із загальнодоступних методів об'єкта є невдалий або відсутній тест.
Майкл Шоу

Це зводиться до тестування на поведінку чи стан. Очевидно, приватні методи потрібно перевірити певним чином, але якщо ви класичний TDD, ви перевірите їх, випробувавши клас "у цілому", тестові інструменти, які більшість людей використовують, зосереджені на тестуванні кожного методу індивідуально ... і моя думка полягає в тому, що останні фактично не тестуються належним чином, оскільки вони втрачають можливість пройти повний тест. Тож я погоджуюсь з вами, намагаючись підкреслити, як TDD підрив те, як деякі (більшість?) Людей сьогодні проходять одиничне тестування.
gbjbaanb

1

Ін'єкція залежності також робить зрозумілішим, що ваш об'єкт має залежності.

Коли хтось інший наївно використає ваш об’єкт (не дивлячись на конструктор), він виявить, що їм потрібно буде налаштувати служби, з'єднання тощо. Це було не очевидно, оскільки їм не довелося передавати їх конструктору . Перехід залежностей стає зрозумілішим, що потрібно.

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


0

Я погоджуюся з обома таборами тут, і на мою думку це питання все ще відкрите для дискусій. YAGNI вимагає, щоб я не брався за зміну коду, щоб він відповідав припущенню. Одине тестування тут не є проблемою, оскільки я твердо вірю, що залежність ніколи не зміниться. Але якби я проводив тестування підрозділу так, як мав почати, я б ніколи не досяг цього. Як я б врешті-решт скоротив свій шлях до DI.

Дозвольте запропонувати ще одну альтернативу для мого сценарію

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

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