Комментарии 83
Помните один из минусов слабо связанных архитектур? Из-за того, что в них преобладает позднее связывание
Не стоит, все-таки, путать слабое связывание и позднее связывание. Можно иметь слабо связанную архитектуру, построенную полностью на раннем связывании.
Вызов метода DoHeavyBreath у класса Luke — логическая ошибка не видимая компилятору.
Что? Если предположить, что это C#, то компилятор радостно бросит тут ошибку.
(а вот ваш второй пример, с двумя оверрайдами — это как раз логическая ошибка проектирования)
Но теперь мы можем воспользоваться TDD для обнаружения этой ошибки.
А что нам мешало сделать это раньше?
Мы создаем метод для тестирования метода main. Простейшим способом
Простейший способ — это не ловить ошибку вообще. Зачем вам over-specification?
В общем, ничего не имею против TDD, но вот ваша статья как-то совершенно ни о чем.
пример можно бесконечно улучшать, чего я не делал, а просто показал плюс один довод в пользу TDD. Если раньше я рассматривал его как полезный инструмент, а теперь вижу как необходимый и попытался обьяснить почему.
Иногда когда рождаются какие-то идеи, то у меня привычка гуглить, не додумался ли кто-то до такого же, а не кричать, что я первый что-то придумал. Просто интересно не возникало ли еще у кого-то подобных ассоциаций. Интересно послушать.
Иногда когда рождаются какие-то идеи, то у меня привычка гуглить, не додумался ли кто-то до такого же, а не кричать, что я первый что-то придумал. Просто интересно не возникало ли еще у кого-то подобных ассоциаций. Интересно послушать.
Я не вижу в вашей статье ни одного реального аргумента в пользу TDD. Во-первых, потому, что ваш пример ловится компилятором, а во-вторых, потому, что даже для ошибок позднего связывания достаточно интеграционных тестов (напоминаю, что TDD и покрытие кода тестами — не одно и то же).
Можете рассматривать это не как довод, а как другой взгляд. Это просто мои ассоциации, которые я посчитал могут быть интересными, и захотел услышать того, кто думает так же.
И можно поподробнее, что именно ловится компилятором?
И можно поподробнее, что именно ловится компилятором?
А, извините, я был невнимателен. Не ловится (вызов метода DoHeavyBreath у класса Luke). Но его и не должно быть, потому что класс Luke не является наследником класса Dart, чтобы вы ни думали про SW.
Так что это типичная ошибка проектирования (если конкретнее, нарушение LSP), и TDD тут не при чем.
Так что это типичная ошибка проектирования (если конкретнее, нарушение LSP), и TDD тут не при чем.
Я вылечился немного от перфекционизма, поэтому получился такой пример :)
Если хотите, могу придумать для Вас более адекватный. Просто подумал, что такого достаточно, чтоб уловить суть идеи.
Если хотите, могу придумать для Вас более адекватный. Просто подумал, что такого достаточно, чтоб уловить суть идеи.
Ох уж эти приверженцы юнит-тестов…
«Писать тесты» != «Практиковать TDD»
Про само TDD в статье ни слова.
Про само TDD в статье ни слова.
Наверное мне было очевидно, что если я пишу про покрытие тестами и упоминаю в заголовке TDD, то я и имею его в виду. Написать тесты конечно не достаточно, надо еще и запускать их. Вообще если придираться, то я бы сказал, что статья про continuous testing, но все это наиболее органично смотрится в сочетании с TDD. Не хотелось расписывать все подробно (может еще и определение TDD привести?).
Из всех коментариев на данный момент, я еще не услышал комментария по сути, а только цепляния за какие-то мелочи. Ну не нравится или непонятна статья — спросите или проходим дальше по салону.
Из всех коментариев на данный момент, я еще не услышал комментария по сути, а только цепляния за какие-то мелочи. Ну не нравится или непонятна статья — спросите или проходим дальше по салону.
Я про тесты узнал как раз из-за того, что часто встречал упоминание ТДД и, конечно, и сам с этого начал. Сейчас я не представляю, как можно обходиться без тестов, но ТДД я не использую и ваше очевидное «тесты => TDD» таковым не считаю.
Это и есть «по сути». Вы собрались об одном рассказать (о ТДД, в заголовке), а рассказали об одном из плюсов написания тестов. Причем, если на тему ТДД есть еще разные мнения, то на счет тестов, вроде, все понятно и однозначно: тесты — это хорошо.
В итоге, получилось капитанство — вроде и разумные вещи сказали, а в чем цель была — не понятно.
Это и есть «по сути». Вы собрались об одном рассказать (о ТДД, в заголовке), а рассказали об одном из плюсов написания тестов. Причем, если на тему ТДД есть еще разные мнения, то на счет тестов, вроде, все понятно и однозначно: тесты — это хорошо.
В итоге, получилось капитанство — вроде и разумные вещи сказали, а в чем цель была — не понятно.
Не стоит всех записывать в дураки :) Просто TDD — это идиология, подход к разработке, а не сами тесты.
А то, что описали Вы — всё равно что описывать преимущества ООП, на основе того, что в IDE функции/поля легче набираются
А то, что описали Вы — всё равно что описывать преимущества ООП, на основе того, что в IDE функции/поля легче набираются
Как-то всё очень сумбурно, а главное не видно преимуществ именно TDD, а не просто покрытиями тестами.
С другой стороны, тест и есть декларативное (практически) описание бизнес-логики. Я бы больше ожидал генерации кода по тестам, чем тестов по более высокоуровнему коду. Тем более на языках типа руби код тестов и с ипользованием тестирующих фреймворков зачастую и так выглядит как естественный язык.
С другой стороны, тест и есть декларативное (практически) описание бизнес-логики. Я бы больше ожидал генерации кода по тестам, чем тестов по более высокоуровнему коду. Тем более на языках типа руби код тестов и с ипользованием тестирующих фреймворков зачастую и так выглядит как естественный язык.
Если я не ошибаюсь, примеры говорят о пользе юнит тестов.
А про TDD (test driven development — разработка через тестирование, в котором СНАЧАЛА пишутся тесты, а после, по ним разрабатывается код) здесь реально ничего нет
А про TDD (test driven development — разработка через тестирование, в котором СНАЧАЛА пишутся тесты, а после, по ним разрабатывается код) здесь реально ничего нет
Папа и мама TDD, Kent Beck: stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565
Короткий перевод: «мне платят за то, чтобы я писал рабочий код, а не писал тесты. Тесты пишу по необходимости».
;)
Короткий перевод: «мне платят за то, чтобы я писал рабочий код, а не писал тесты. Тесты пишу по необходимости».
;)
чтобы я писал рабочий код
в этом Вам и помогут юнит-тесты, потому что на каком-то этапе сложности архитектуры начнете спотыкаться
Юнит-тесты (сами по себе) никак (вообще никак) не влияют на архитектуру (в сторону ее улучшения). Более того, периодически юнит-тесты архитектуру только усложняют (совершенно без необходимости).
Снижение связанности — это уже плохо?
Это никак (при отсутствии требований на конкретные параметры приложения). Не говоря уже о том, что сами по себе юнит-тесты его не то что бы гарантируют.
Ок, что ты тогда считаешь хорошей архитектурой?
Ту, которая оптимальным образом решает поставленную бизнес-задачу с учетом существующих ограничений.
(привет Казману, однозначно)
(привет Казману, однозначно)
Критерий оптимальности? Мы учитываем только бизнес-задачу, или еще и задачи поддержки, тестирования и прочего бла-бла-бла?
Задачи поддержки, тестируемости и прочего бла-бла либо входят в бизнес заказчика (нам нужно приложение, которое будет работать у нас десять лет и меняться), либо в бизнес разработчика (нам нужно иметь возможность изменить за недорого для себя). И балансировка по этим критериям как раз и определяется бизнес-задачей.
Экстремальный пример: если мы пишем одноразовый скрипт, выгружающий что-то из БД, в нем не надо делать слабо связанную архитектуру на три слоя. Но это (а) программа (б) решающая конкретную задачу (ц) в условиях конкретных ограничений.
Мы привыкли не думая вносить модульность, расширяемость, заменяемость и прочие блабла в список обязательных атрибутов решения; но бизнесу это не надо. И за юнит-тесты бизнес тоже не платит (как бы мы их не любили).
Ну и да, типичный пример навязанных юнит-тестами ограничений — это, конечно, разрыв зависимостей со временем (вообще никак не нужный для хорошей архитектуры).
Экстремальный пример: если мы пишем одноразовый скрипт, выгружающий что-то из БД, в нем не надо делать слабо связанную архитектуру на три слоя. Но это (а) программа (б) решающая конкретную задачу (ц) в условиях конкретных ограничений.
Мы привыкли не думая вносить модульность, расширяемость, заменяемость и прочие блабла в список обязательных атрибутов решения; но бизнесу это не надо. И за юнит-тесты бизнес тоже не платит (как бы мы их не любили).
Ну и да, типичный пример навязанных юнит-тестами ограничений — это, конечно, разрыв зависимостей со временем (вообще никак не нужный для хорошей архитектуры).
Перефразирую твой комментарий:
1. «Не забивайте гвозди микроскопами». Спасибо, кэп.
2. «Софт должен зарабатывать деньги для бизнеса, а не ЧСВ для разработчиков». Еще раз спасибо, кэп.
Только, имхо, к теме это имеет отдаленное отношение. Только не надо демагогии про то, что архитектура одноразового скрипта — это тоже архитектура ;)
Уточню scope — я выше говорил о типичных «больших энтерпрайзненьких проектах».
С какого перепуга разрыв зависимостей и снижение cohesion — это плохо?
1. «Не забивайте гвозди микроскопами». Спасибо, кэп.
2. «Софт должен зарабатывать деньги для бизнеса, а не ЧСВ для разработчиков». Еще раз спасибо, кэп.
Только, имхо, к теме это имеет отдаленное отношение. Только не надо демагогии про то, что архитектура одноразового скрипта — это тоже архитектура ;)
Уточню scope — я выше говорил о типичных «больших энтерпрайзненьких проектах».
С какого перепуга разрыв зависимостей и снижение cohesion — это плохо?
Так я не говорю, что это плохо, я говорю, что само по себе это никак. Ну вот реально никак. Потому что ты в плюсах получаешь низкую связность, а в минусах — отсутствие (некоторых) статических проверок и ухудшение читаемости кода. Баш на баш.
Юнит-тесты (по крайней мере, в .net, с его технологической базой) навязывают существенно больший уровень разрыва зависимостей, чем реально нужно для архитектурной целостности системы (а это, заметим, плохо, как любой экстремизм).
Юнит-тесты (по крайней мере, в .net, с его технологической базой) навязывают существенно больший уровень разрыва зависимостей, чем реально нужно для архитектурной целостности системы (а это, заметим, плохо, как любой экстремизм).
Проблема со статическими проверками обходится контрактами, а ухудшение читаемости кода… ну, не знаю. Я пока не видел примеров, где читаемость ухудшалась от снижения связанности.
Я вот регулярно вижу. Просто мы привыкли уже это игнорировать, но в реальности там лишний уровень абстракции.
Как именно лишний слой абстракции снижает читаемость?
Представь себе типичную ситуацию: у тебя есть зависимость, абстрагированная за интерфейс. Теперь, находясь внутри некоего пользователя этой зависимости ты понимаешь, что тебе надо проследить за кодом, который внутри нее, потому что ты не понимаешь, как это работает. Вместо того, чтобы попасть сразу в имплементацию, ты попадаешь в интерфейс (традиционно не документированный), а из него тебе надо перейти в реализацию, и хорошо, если она одна.
Понятно, что этого не бывает в идеальном коде, но идеальный код и читать не надо.
Понятно, что этого не бывает в идеальном коде, но идеальный код и читать не надо.
Стоп-стоп.
Так что снижает читаемость — разрыв зависимости или кривой интерфейс и кривая декомпозиция ответственностей? Кто виноват в том, что мне понадобилось лезть за интерфейс «в черный ящик»?
Так что снижает читаемость — разрыв зависимости или кривой интерфейс и кривая декомпозиция ответственностей? Кто виноват в том, что мне понадобилось лезть за интерфейс «в черный ящик»?
И то, и другое. Кривой интерфейс ведет к тому, что приходится лезть в ящик, абстракция приводит к тому, что это сложнее.
Будь реалистом, интерфейс всегда течет.
Будь реалистом, интерфейс всегда течет.
Интерфейс течет независимо от того как именно мы его дергаем. ИМХО, но слабая связь по моему субъективному впечатлению течет меньше. Точнее у разработчика меньше возможностей устроить протечку или воспользоваться существующей. (я тут уже несколько дней не могу отойти от увиденного HttpContext'а в DAL).
А удобство залезания в черный ящик — это уже скорее вопрос к прикладному инструментарию.
А удобство залезания в черный ящик — это уже скорее вопрос к прикладному инструментарию.
Некрокоммент — вот собственно обратил внимание на днях. Когда я в такой ситуации жму на зависимость правой кнопкой (на вызов метода) и Go To Implementation, то умненький решарпер кидаем меня не в интерфейс, а конкретно в реализацию интерфейса, если она одна или предлагает списочек если их несколько.
Вопрос инструментария короче.
Вопрос инструментария короче.
По-моему, это зависит от самого слоя. Насколько понятна его роль, например. Если у меня был такой код:
А потом Архитектор ввел какой то новый слой, логики которого я не понимаю до конца, то для меня снизится читабельность когда я увижу что-то вроде:
IRepository rep = Factory.Get<IRepository>();
А потом Архитектор ввел какой то новый слой, логики которого я не понимаю до конца, то для меня снизится читабельность когда я увижу что-то вроде:
IRepository rep = Preprocessor.DoSomeStrangeLogic(Factory.Get<IRepository>());
BTW, вот как раз cohesion не надо бездумно снижать, потому что методы в интерфейсе должны иметь высокий семантический cohesion (в том смысле, что методы одного интерфейса должны иметь связанные обязанности, а иначе нахрена они в одном интерфейсе).
Бррр… Конечно, не cohesion, а coupling, сорри.
Я от разрыва зависимостей вижу только пользу — чем меньше кусочки и чем меньше между ними связей (до определенных пределов, конечно) — тем проще управлять системой и заменять/обновлять ее части (старое доброе «разделяй и властвуй»).
А это в итоге имеет прямую ценность для бизнеса в виде снижения издержек на изменения.
Я от разрыва зависимостей вижу только пользу — чем меньше кусочки и чем меньше между ними связей (до определенных пределов, конечно) — тем проще управлять системой и заменять/обновлять ее части (старое доброе «разделяй и властвуй»).
А это в итоге имеет прямую ценность для бизнеса в виде снижения издержек на изменения.
готов поспорить, что этот одноразовый скрипт вы таки тестируете на тестовой базе данных, а не запускаете сразу фигачить начистовую. И какая разница, создавать тестовую БД или написать пару тестов, которые будут проверять правильность работы этого скрипта?
готов поспорить, что этот одноразовый скрипт вы таки тестируете на тестовой базе данных
Поспорьте. Лучше на что-нибудь крупное, чтобы мне было приятнее.
И какая разница, создавать тестовую БД или написать пару тестов, которые будут проверять правильность работы этого скрипта?
Во-первых, тестовая БД все равно есть. А во-вторых, «пара тестов» требуют изменения архитектуры. Вы когда-нибудь пробовали покрывать тестами powershell? А T-SQL?
Сами по себе, да, не влияют. Но если стремиться делать тесты как можно проще, то очень даже влияют. Формально после этого архитектура может и усложнится (больше уровней абстракций появится, больше параметров в функции/методы нужно будет передавать, а не глобальные переменные использовать :) ), но вот вносить в неё изменения будет проще, даже если забыть про функцию тестов «контроль целостности».
ИМХО, больше уровней абстракции не обязательно равно усложнению архитектуры. Нужно отталкиваться от параметров «цикломатическая сложность», «связность» и «связанность», а не от количества уровней и классов.
Но если стремиться делать тесты как можно проще, то очень даже влияют.
У нас с вами явно разное понимание «тестов как можно проще». Опыт показывает, что в более-менее объемном приложении со сложным графом зависимостей сетап теста — штука существенно более сложная, чем сам код.
больше параметров в функции/методы нужно будет передавать
Больше параметров — сложнее сетап — сложнее (а не проще) тест.
но вот вносить в неё изменения будет проще
Это вы исходите из того, что в архитектуру надо будет вносить изменения. А для меня это не догма.
У нас вообще по многим вопросам разное понимание :) Возможно обусловленное масштабами проектов над которыми мы работаем. В частности, сложность тестов я не меряю большим количеством параметров, сложностью их подготовки. Одно дело вызвать в сетапе пускай десяток, но обычных new со скалярными параметрами конструктора, совсем другое подготовить пускай всего один, но сложный стаб, получающий и возвращающий объекты с трехэтажной агрегацией, да ещё и считывающий и меняющий внутренне состояние фэйкового объекта.
Соглашусь с предыдущим ораторами — в тексте ничего про TDD нет.
Данная статья показывает преимущества просто юнит-тестирования. Показывает правда плохо, вы написали только об самом простом аспекте тестирования — отлов необработанных исключений, а основные аспекты темы не раскрыты, а именно:
1. прогнозирование поведения объекта тестирования, когда не все слои приложения сделаны. Допустим вы написали бизнес-логику и слой доступа к базам данных, а веб-приложения еще не написано, или когда работаете в команде и вам надо проверить вашу часть кода.
2. написание теста для класса хорошо показывает насколько независимым вы сделали ваш объект ( DI и IoC)
3. тесты очень хорошо помогают, если вам приходится разбираться в чужом коде (да и в своем кстати тоже). Если мне достается чей-то проект (для редактирования или кодревью) и там есть написанные юнит-тесты — это как бальзам на душу.
и тд…
По поводу TDD это отдельная тема, чесно говоря, если про юнит-тестирование я могу сказать, что это всегда хорошо, то TDD от случая к случаю — все зависит от поставленных задач и от общего стиля работы команды разарботчиков.
Данная статья показывает преимущества просто юнит-тестирования. Показывает правда плохо, вы написали только об самом простом аспекте тестирования — отлов необработанных исключений, а основные аспекты темы не раскрыты, а именно:
1. прогнозирование поведения объекта тестирования, когда не все слои приложения сделаны. Допустим вы написали бизнес-логику и слой доступа к базам данных, а веб-приложения еще не написано, или когда работаете в команде и вам надо проверить вашу часть кода.
2. написание теста для класса хорошо показывает насколько независимым вы сделали ваш объект ( DI и IoC)
3. тесты очень хорошо помогают, если вам приходится разбираться в чужом коде (да и в своем кстати тоже). Если мне достается чей-то проект (для редактирования или кодревью) и там есть написанные юнит-тесты — это как бальзам на душу.
и тд…
По поводу TDD это отдельная тема, чесно говоря, если про юнит-тестирование я могу сказать, что это всегда хорошо, то TDD от случая к случаю — все зависит от поставленных задач и от общего стиля работы команды разарботчиков.
Darth, а не Dart
Спасибо за интересную мысль. Мысль рассматривать тесты как навесные проверки для своего кода мне почему-то в голову не приходила. Возможно, потому что тесты — штука очень конкретная. Если дополнить непрерывный прогон тестов генерацией этих самых тестов по определенным правилам, будет интереснее. Поясню свою мысль — есть у нас интерфейс. Написали тесты, которые проверяют его семантику. Я хочу, чтобы эти тесты выполнялись для каждой реализаии интерфейса, появляющейся в проекте. Без лишних телодвижений со стороны разработчикОВ.
Я хочу, чтобы эти тесты выполнялись для каждой реализаии интерфейса, появляющейся в проекте
А как же сетап, который для каждой реализации может быть свой?
И это только одна из проблем, согласен. Не судите строго, идея пришла мне в голову после прочтения статьи, надо ещё обдумать. Есть вроде инструмент, которые автоматически генерируют код, создающий объект заданного типа.
Есть вроде инструмент, которые автоматически генерируют код, создающий объект заданного типа.
Имя им легион (причем даже уже код генерировать не надо). Проблема, однако же, в том, что мало создать объект, надо его еще сконфигурировать (передать зависимости, сконфигурированные на нужное поведение). И эта конфигурация для каждого объекта мало того, что своя, так еще и может отличаться для разных тестов.
Можно название двух-трех таких инструментов?
Создания объекта заданного типа?
Activator.CreateInstance() встроен в .net framework с первой версии.
Activator.CreateInstance() встроен в .net framework с первой версии.
Нет, я просто неправильно понял. Я подумал об инструментах, генерирующих код по юнит тестам.
Вы имеете в виду работающий код? Это, знаете ли, плохо решаемая задача, потому что это то же самое, что автоматически писать программы по требованиям.
А просто декларации, соответствующие использованию, генерятся в VS начиная с прошлой, что ли, версии (Generate Method Stub).
А просто декларации, соответствующие использованию, генерятся в VS начиная с прошлой, что ли, версии (Generate Method Stub).
Ну мне лично кажется, что это осуществимо при достаточной формальности и полноты описания бизнес логики. Может не вся программа на 100% будет генерироваться, но какие то блоки могут быть автоматически сгенерированы. Это только мысли и догадки.
В этом случае просто ваше «формальное и полное описание бизнес-логики» и будет тем кодом, который надо будет тестировать. Проблема никуда не исчезнет, а просто будет перенесена в другой уровень (который, заметим, ничем не лучше «просто кода»).
Для таких вещей делают DSL (код на котором, естественно, тоже тестируют).
Для таких вещей делают DSL (код на котором, естественно, тоже тестируют).
После обсуждения с коллегами идея формулируется так:
Автоматическая привязка тестов к коду. Для теста задается условие (шаблон или как там в АОП, поинткат?), при выполнении которого устанавливаются значения некоторых переменных и происходит запуск теста (переменные могут в тесте использоваться). Если при этом не хватает какого-то кода привязки, который не может быть сгенерирован — тест не проходит.
Пример 1: шаблон такой: для каждого метода с именем $© get${F}() в классе ${T}, если существует метод set${F}( ${C} ), выполняем такой тест:
${T} t = create${T}();
${C} f = create${C}();
t.set${F}( f );
assertTrue( t.get${F}() == f );
Если методы create для T и С не найдены — тест не проходит.
Пример 2: для каждого интерфейса ${I} и класса ${C}, который его реализует, надо выполнить заданный тест:
void test( ${I} i ) {
…
}
с параметром create${C}
методы create на самом деле могут быть итераторами, то есть возвращать набор значений, а использовать надо все комбинации.
наверное, возможностей create хватит не всегда, надо еще какой-то механизм придумать
Автоматическая привязка тестов к коду. Для теста задается условие (шаблон или как там в АОП, поинткат?), при выполнении которого устанавливаются значения некоторых переменных и происходит запуск теста (переменные могут в тесте использоваться). Если при этом не хватает какого-то кода привязки, который не может быть сгенерирован — тест не проходит.
Пример 1: шаблон такой: для каждого метода с именем $© get${F}() в классе ${T}, если существует метод set${F}( ${C} ), выполняем такой тест:
${T} t = create${T}();
${C} f = create${C}();
t.set${F}( f );
assertTrue( t.get${F}() == f );
Если методы create для T и С не найдены — тест не проходит.
Пример 2: для каждого интерфейса ${I} и класса ${C}, который его реализует, надо выполнить заданный тест:
void test( ${I} i ) {
…
}
с параметром create${C}
методы create на самом деле могут быть итераторами, то есть возвращать набор значений, а использовать надо все комбинации.
наверное, возможностей create хватит не всегда, надо еще какой-то механизм придумать
Пример 1
А зачем это тестировать? Это тривиальный код (не говоря уже о том, кто будет писать методы create, особенно учитывая, что они могут быть для разных тестовых случаев)
Пример 2
И снова — кто будет писать конкретные тестовые фабрики? Вся сложность-то как раз в них, недаром Мезарос столько внимания уделяет как раз разным вариантм fixture.
А так, что, что вы описываете — типичный параметризованный тест, все давно съедено и придумано. У меня так контроллеры проверяются на то, что все зависимости доступны.
Цель примера — продемонстрировать идею. Как правило, он получается слишком простым. Но в более сложном примере сложнее разобраться.
При появлении нового контроллера ваши тесты перестают проходить?
При появлении нового контроллера ваши тесты перестают проходить?
Цель примера — продемонстрировать идею. Как правило, он получается слишком простым. Но в более сложном примере сложнее разобраться.
Проблема в том, что из вашего простого примера совершенно не понятно, как решать сложные ситуации (а именно они нуждаются в тестировании в первую очередь).
При появлении нового контроллера ваши тесты перестают проходить?
Да.
Да, наверное, fixture — это обобщение моего create
C помощью какого мехнизма тесты перестают проходить при появлении нового контроллера?
C помощью какого мехнизма тесты перестают проходить при появлении нового контроллера?
C помощью какого мехнизма тесты перестают проходить при появлении нового контроллера?
Параметризованный тест + autodiscovery исходных данных. Если бы MsTest умел — был бы вообще data-driven с кастомным поставщиком, но, к сожалению, это не так просто, как хотелось бы.
Спасибо за первый комментарий по сути. Да, мысль о генерации кода по тестам мне еще не приходила, довольно интересно. И думаю, что реализация не за горами.
А что мешает объявить функции абстрактными, в данном конкретном примере?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Почему Вы должны сейчас все бросить и начать писать юнит тесты