Найкращі практики розробки з використанням тестів із використанням C # та RhinoMocks [закрито]


86

Для того, щоб допомогти своїй команді написати тестований код, я придумав цей простий перелік найкращих практик для того, щоб зробити нашу базу коду C # більш перевірочною. (Деякі пункти стосуються обмежень Rhino Mocks, насмішкуватої основи для C #, але правила можуть застосовуватись і більш загально.) Чи є у когось найкращі практики, яких вони дотримуються?

Щоб максимально перевірити код, дотримуйтесь наступних правил:

  1. Спочатку напишіть тест, а потім код. Причина: Це гарантує, що ви пишете перевіряється код і що кожен рядок коду отримує тести, написані для нього.

  2. Класи проектування з використанням введення залежностей. Причина: Ви не можете знущатися чи перевіряти те, що не видно.

  3. Відокремте код інтерфейсу від його поведінки за допомогою Model-View-Controller або Model-View-Presenter. Причина: Дозволяє перевіряти ділову логіку, тоді як деталі, які не можна перевірити (UI), мінімізовані.

  4. Не пишіть статичні методи або класи. Причина: Статичні методи важко або неможливо виділити, і Rhino Mocks не може знущатися над ними.

  5. Програмуйте інтерфейси, а не класи. Причина: Використання інтерфейсів уточнює взаємозв'язки між об'єктами. Інтерфейс повинен визначати послугу, яка потрібна об’єкту з його оточення. Крім того, над інтерфейсами можна легко знущатися, використовуючи Rhino Mocks та інші насмішливі фреймворки.

  6. Виділіть зовнішні залежності. Причина: невирішені зовнішні залежності не можна перевірити.

  7. Позначте як віртуальні методи, над якими ви збираєтеся знущатися. Причина: Rhino Mocks не може знущатися над невіртуальними методами.


Це корисний список. В даний час ми використовуємо NUnit і Rhino.Mocks, і добре прописати ці критерії для членів команди, які менш знайомі з цією стороною модульного тестування.
Кріс Баллард

Відповіді:


58

Безумовно, хороший список. Ось кілька думок щодо цього:

Спочатку напишіть тест, а потім код.

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

Класи проектування з використанням введення залежностей.

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

Відокремте код інтерфейсу від його поведінки за допомогою Model-View-Controller або Model-View-Presenter.

Домовились. Зверніть увагу, що навіть ведучий / контролер може бути протестований за допомогою DI / IoC, передавши йому затьмарений / глузливий вигляд та модель. Перевірте Presenter First TDD, щоб дізнатися більше про це.

Не пишіть статичні методи або класи.

Не впевнений, що я з цим згоден. Можливо юніт-тестування статичного методу / класу без використання макетів. Тож, можливо, це одне із тих специфічних правил Rhino Mock, про які ви згадали.

Програмуйте інтерфейси, а не класи.

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

Виділіть зовнішні залежності.

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

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

Це обмеження Rhino Mocks. У середовищі, яке віддає перевагу ручно закодованим заглушкам перед макетним об'єктом, це не буде необхідним.

І ще кілька нових пунктів, які слід врахувати:

Використовуйте креативні шаблони дизайну. Це допоможе з DI, але також дозволяє ізолювати цей код і протестувати його незалежно від іншої логіки.

Напишіть тести, використовуючи техніку Білла Вейка Arrange / Act / Assert . Ця техніка чітко дає зрозуміти, яка конфігурація необхідна, що насправді тестується та що очікується.

Не бійтеся закочувати власні знущання. Часто ви виявите, що використання макетних фреймворків робить ваші тести неймовірно важкими для читання. Прокачуючи власні, ви будете мати повний контроль над своїми макетами / заглушками, і ви зможете тримати свої тести читабельними. (Зверніться до попереднього пункту.)

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

Впровадити безперервну інтеграцію. Реєструйте свій код на кожній "зеленій панелі". Створюйте програмне забезпечення та запускайте повний набір модульних тестів під час кожної реєстрації. (Звичайно, це не є практикою кодування як такою; але це неймовірний інструмент для забезпечення чистоти та повної інтеграції програмного забезпечення.)


3
Зазвичай я виявляю, що якщо тест важко читати, виною не фреймворк, а код, який він тестує. Якщо SUT складний у налаштуванні, то, можливо, його слід розбити на більше понять.
Steve Freeman

10

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

Ознайомтеся з цим швидким початком, щоб побачити, наскільки інтуїтивнішими стають ваші тести, ось простий приклад:

// ShouldExpectMethodCallWithVariable
int value = 5;
var mock = new Mock<IFoo>();

mock.Expect(x => x.Duplicate(value)).Returns(() => value * 2);

Assert.AreEqual(value * 2, mock.Object.Duplicate(value));

5
Я думаю, що нова версія Rhino Mocks теж працює так
Джордж Мауер


3

Це дуже корисна публікація!

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

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

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

Я схильний вважати, що основне значення TDD знаходиться в межах (чорний ящик), а також у випадкових тестуваннях білих скриньок критично важливих областей системи.


2

Справжня причина програмування проти інтерфейсів полягає не в тому, щоб полегшити життя Rhino, а в тому, щоб з'ясувати взаємозв'язки між об'єктами в коді. Інтерфейс повинен визначати послугу, яка потрібна об’єкту з його оточення. Клас забезпечує конкретну реалізацію цієї послуги. Прочитайте книгу Ребекки Вірфс-Брок "Дизайн об'єктів" про ролі, обов'язки та співавторів.


Погоджено ... Я збираюся оновити своє запитання, щоб відобразити це.
Кевін Альбрехт,

1

Хороший список. Однією з речей, яку ви, можливо, захочете встановити - і я не можу дати вам багато порад, оскільки я тільки починаю про це думати - це коли клас повинен знаходитися в іншій бібліотеці, просторі імен, вкладених просторах імен. Можливо, ви навіть захочете заздалегідь з’ясувати список бібліотек та просторів імен та доручення, з яким команда повинна зібратися, і вирішити об’єднати дві / додати нову.

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


Я також роблю подібний розділ тестування стилю BDD (на додаток до коду модульного тестування) в особистому проекті.
Кевін Альбрехт,

0

Ось ще один, про який я подумав, що мені подобається робити.

Якщо ви плануєте запускати тести з модульного тестування Gui, на відміну від TestDriven.Net або NAnt, то мені було простіше встановити тип проекту модульного тестування на консольний додаток, а не на бібліотеку. Це дозволяє запускати тести вручну та проходити їх у режимі налагодження (що вищезгаданий TestDriven.Net насправді може зробити за вас).

Крім того, мені завжди подобається відкривати проект Playground для тестування бітів коду та ідей, які мені незнайомі. Це не слід перевіряти в контролі джерела. Ще краще, він повинен знаходитися в окремому сховищі джерел керування лише на машині розробника.

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