Mvc 5, модульне тестування
У цій статті ми маємо намір користуватися вбудованою підтримкою модульного тестування, пропонованої Visual Studio, хоча доступні й інші пакети модульного тестування .NET. Найбільш популярним з них є, мабуть, NUnit. проте все пакети тестування в основному роблять одне і те ж. Причина вибору інструментів тестування Visual Studio пов'язана з привабливістю інтеграції з іншими частинами IDE-середовища.
Для роботи з вбудованими засобами модульного тестування Visual Studio в приклад проекту (який ми почали розробляти в попередній статті) була додана нова реалізація інтерфейсу IDiscountHelper. Створіть в папці Models новий файл на ім'я MinimumDiscountHelper.cs з вмістом, наведеними в прикладі нижче:
Наша мета в цьому прикладі - змусити клас MinimumDiscountHelper продемонструвати наступні аспекти поведінки:
якщо загальна сума більше $ 100, знижка становитиме 10%;
якщо загальна сума знаходиться в проміжку між $ 10 і $ 100 включно, знижка становитиме $ 5;
для загальної суми, що не перевищує $ 10, знижка не надається;
для негативної загальної суми буде згенеровано виняток ArgumentOutOfRangeException.
Клас MinimumDiscountHelper поки ще не реалізує жодного з перерахованих аспектів поведінки. Ми будемо слідувати підходу розробки через тестування (Test Driven Development - TDD). спочатку написавши модульні тести і тільки потім реалізував код.
Створення проекту модульного тестування
Перший крок полягає в створенні проекту модульного тестування, для чого у вікні Solution Explorer клацніть правою кнопкою миші на елементі верхнього рівня (Рішення (Solution)) і виберіть в контекстному меню пункт Add -> New Project (Додати -> Новий проект).
На необхідність створення проекту тестування можна також вказати при створенні нового проекту MVC: в діалоговому вікні, де вибирається початкове вміст для проекту MVC. Для цього передбачений прапорець Add Unit Tests (Додати модульні тести).
Відкриється діалогове вікно Add New Project (Додавання нового проекту). У розділі шаблонів Visual C # в лівій панелі виберіть елемент Test (Тестування) і переконайтеся, що в середньої панелі обраний варіант Unit Test Project (Проект модульного тестування), як показано на малюнку:

Вкажіть EssentialTools.Tests як ім'я проекту і клацніть на кнопці OK, щоб створити новий проект, який буде додано до поточне рішення Visual Studio поряд з проектом програми MVC.
Проекту тестування необхідно надати посилання на проект програми, щоб отримати доступ до класів для виконання стосовно до них тестів. У вікні Solution Explorer клацніть правою кнопкою миші на елементі References для проекту EssentialTools.Tests і виберіть в контекстному меню пункт Add Reference (Додати посилання). Клацніть на елементі Solution (Рішення) в лівій панелі і відзначте прапорець поруч з EssentialTools:

Створення модульних тестів
Для початку внесіть зміни, показані в прикладі нижче:
Тут був доданий одиночний модульний тест. Клас, який містить тести, анотований атрибутом TestClass. а окремі тести являють собою методи, анотовані атрибутом TestMethod. Не всі методи в тестовому класі повинні бути модульними тестами. Для демонстрації сказаного ми визначили метод getTestObject (), який буде використовуватися для організації тестів. Оскільки цей метод не має атрибута TestMethod, середа Visual Studio НЕ буде трактувати його як модульний тест.
Зверніть увагу на додавання оператора using для імпорту простору імен EssentialTools.Models в тестовий клас. Тестові класи - це всього лише звичайні класи C #, які абсолютно не обізнані про проект MVC. Всю "магію" тестування в проекті забезпечують атрибути TestClass і TestMethod.
Як бачите, при створенні цього методу модульного тесту ми йшли шаблоном "організація / дію / твердження" (arrange / act / assert - A / A / A). який був описаний в статті "Автоматизоване тестування".
Існує величезна кількість угод по іменування модульних тестів, але ми дотримуємося принципу призначення таких імен, які ясно відображають те, що перевіряється тестом. Наш метод модульного тесту називається Discount_Above_100 () (знижка на суму вище $ 100) і виглядає для нас чітко і ясно. Проте, в дійсності важливим є лише те, щоб ви (і ваша команда) розуміли прийнятий шаблон іменування, тому можете вибрати інший підхід, якщо даний чимось не влаштовує.
На початку тестового методу ми викликаємо метод getTestObject (), який створює екземпляр об'єкта, призначеного для тестування - в даному випадку це MinimumDiscountHelper. Крім того, ми визначаємо значення total, для якого буде проводитися тестування. Це розділ організації модульного тесту.
У розділі дії тесту ми викликаємо метод MinimumDiscountHelper.ApplyDiscount () і присвоюємо повертається їм результат змінної discountedTotal. Нарешті, в розділі затвердження тесту ми застосовуємо метод Assert.AreEqual () для перевірки того, що значення, отримане з методу ApplyDiscount (), становить 90% загальної суми, зазначеної на початку.
У класі Assert визначено набір статичних методів, які можна використовувати в тестах. Цей клас знаходиться в просторі імен Microsoft.VisualStudio.TestTools.UnitTesting разом з низкою додаткових класів, корисних для настройки і виконання тестів.
Клас Assert є одним з найбільш часто вживаних, тому його важливі методи коротко описані в таблиці нижче:
Статичні методи класу Assert
Стверджує, що об'єкт не належить до зазначеного типу
Кожен статичний метод в класі Assert дозволяє перевірити якийсь аспект модульного тесту, і якщо перевірка не проходить, ці методи генерують виняток. Щоб модульний тест пройшов, всі твердження повинні завершитися успішно.
Кожен метод з таблиці має перевантажену версію, яка приймає параметр string. У разі негативного результату затвердження цей рядок поміщається в елемент повідомлення всередині об'єкта виключення. Методи AreEqual і AreNotEqual мають кілька перевантажених версій, призначених для порівняння специфічних типів. Наприклад, існує версія, яка дозволяє порівнювати рядки без урахування регістру символів.
Одним вартим уваги членом простору імен Microsoft.VisualStudio.TestTools.UnitTesting є атрибут ExpectedException. Це твердження, яке дає позитивний результат, тільки якщо модульний тест генерує виняток з типом, зазначеним в параметрі ExceptionType. Даний атрибут служить надійним способом забезпечення генерації виключень без необхідності в наявності блоків try. catch всередині коду модульного тесту.
Тепер, коли було показано, як створити один модульний тест, до тестового проекту можна додати подальші тести, призначені для перевірки інших аспектів поведінки класу MinimumDiscountHelper. Все додавання показані в прикладі нижче, але ці модульні тести настільки короткі і прості (в цілому це є характерною особливістю модульних тестів), що докладні пояснення для них призводить не будуть:
Проходження (і не проходження) модульних тестів
Середа Visual Studio надає вікно Test Explorer (Провідник тестів). призначене для управління і виконання тестів. Виберіть пункт Windows -> Test Explorer в меню Test середовища Visual Studio, щоб відкрити це вікно, і клацніть на кнопці Run All (Запустити всі) у верхньому лівому кутку. Ви побачите результати, схожі на показані на малюнку нижче:

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

Середа Visual Studio завжди намагається переносити найбільш корисну інформацію в верхню частину вікна Test Explorer. У даній ситуації це означає, що тести, які не пройшли, відображаються перед минулими тестами.
На малюнку видно, що три модульних тесту пройшли, але є проблема, виявлена тестовим методом Discount_Between_10_And_100. Клацнувши на цьому тесті можна з'ясувати, що тест очікував отримати результат 5, тоді як в дійсності було отримано значення 10.
У цей момент ми повертаємося до коду і бачимо, що очікуване поведінка не було реалізовано належним чином - зокрема, знижки для загальних сум, рівних 10 або 100, обробляються некоректно. Проблема криється в наступному операторі з класу MinimumDiscountHelper:
Специфікація, з якою ми маємо справу, встановлює поведінку для значень з проміжку між $ 10 і $ 100 включно, але наша реалізація виключає ці значення і перевіряє тільки величини, які більше $ 10, не враховуючи загальну суму, в точності дорівнює $ 10. Рішення виглядає досить просто і показано в прикладі нижче - для зміни результату дії оператора if знадобиться додати всього один символ:
Після клацання на кнопці Run All у вікні Test Explorer результати показують, що проблема усунена, і всі тести на коді проходять успішно:
