В этом видео я разрабатываю приложение с помощью TDD на языке C#. Кроме демонстрации того, как надо писать модульные тесты, я постарался показать, как работает TDD на уровне приложения в целом.
При разработке применил принцип инверсии зависимости, а также использовал IoC-контейнер.
Ссылки:
Спасибо за видео, очень понравилось, как впрочем и весь другие статьи!
ОтветитьУдалитьВопрос:
Почему желательно интерфейсы и реализацию размещать в разных сборках?
И кстати, там небольшая неточность в презентации, на слайде с заголовком "Принцип инверсии зависимости" SimpleReportSender использует IMailer а не IReportsRepository.
@Михаил
ОтветитьУдалитьИнтерфейсы нужно размещать рядом с тем кодом, который их использует. А реализации интерфейсов отдельно.
В презентации видно, что предполагалось несколько видов отправителей сообщений: SmsReportSender и SmtpReportSender. Если мы объявим интерфейс и обе его реализации в одном месте, то получиться, что клиенты, которые используют *только* SmsReportSender получат в наследство еще и SmtpReportSender, в котором они абсолютно не нуждаются:) и наоборот.
Если же мы объявим в разных сборках, то можем поставлять нашим клиентам только необходимые им реализации интерфейсов.
Подробнее об это можно почитать в книге Роберта Мартина Agile Software Development. В главе 20 принципы упаковки программных проектов.
@Александр
ОтветитьУдалитьОпять слишком большими шагами двигался:)
Ясно, спасибо за ответ
ОтветитьУдалить@Михаил
ОтветитьУдалитьВ добавление к словам Сани хочу дать ссылку http://blog.byndyu.ru/2009/12/blog-post.html#inverted-architecture
Здесь видно, что сборки фактически общаются с интерфейсами других сборок.
Об это было написано у Фаулера в книге "Архитектура корпоративных приложений", вот выдержка http://martinfowler.com/eaaCatalog/separatedInterface.html
"И кстати, там небольшая неточность в презентации..."
Да, спасибо. Теперь я понимаю откуда берутся забавные кино-ляпы ;)
Не совсем понятна роль IoC. Вы говорите, что не будем хардкодить инициализацию объекта в конструкторе, а затем пишете 3 строки кода для IoC. Это не является хардкодом? Ведь, если что-то необходимо будет изменить, придется править не код в конструкторе, а код выше?...
ОтветитьУдалитьХорошее видео. Все здорово.
ОтветитьУдалитьВот только, возможно, про IMailer и IReportRepository - это лишнее, т.к. для людей абсолютно не знакомых с проектированием (или только делающих первые шаги в нем) может быть непонятно что да как. Хотя, бесспорно, их применение абсолютно оправдано )))
@s-stude
ОтветитьУдалитьСпасибо за вопрос. Действительно в масштабах такого маленького проекта трудно оценить роль IoC контейнера.
Но представьте себе, что у вас есть 50-60 объектов типа Reporter и с десяток репозиториев. Они все используются в разных частях проекта. В этом случае будет очень удобно выставить зависимости один раз и в одном месте.
@s-stude типичные IoC контейнеры поддерживают внешнюю конфигурацию, обычно в xml формате.
ОтветитьУдалитьСпасибо за видео, очень понравилось. Есть пара вопросиков: 1) Что следует выносить в проект бизнес логики, а что в проект домена? 2) Вы говорил об огромном (~1200) количестве тестов в серьезном приложении. Каким образом грамотно их структурировать? Ведь можно создать один класс с десятком тестов. А можно создать десять классов, в каждом по одному тесту.
ОтветитьУдалить@Виктор
ОтветитьУдалить"Что следует выносить в проект бизнес логики, а что в проект домена?"
На этот вопрос довольно трудно дать однозначный ответ. Вообще ответ звучит так: в бизнес логику класс бизнес логики, а в домен классы домена. Что есть что в вашем приложении решать вам. Чтобы лучше ориентироваться в этом вопросе советую прочитать книжку по DDD.
"Каким образом грамотно их структурировать?"
На каждую сборку есть сборка модульных тестов. На каждый класс есть тестовый класс. В этом классе все тесты, которые тестируют логику данного класса. Обычно в одном тестового классе около 5-10 тестов.
Большое спасибо за ответы. Книжку по DDD как раз читаю в данное время )), вот и решил посоветоваться с человеком, который уже использует это на практике.
ОтветитьУдалить@Виктор
ОтветитьУдалитьУспехов в освоении! =)
Очень хороший доклад для начинающих. Первая половина - просто песня, оч. хорошо рассказано и оч. подробно. Но вот во второй половине вы слишком быстро рассказали про дальнейшую реализацию. Боюсь что новички не поймут. И IoC у вас получился как то сумбурно, вообще непонятно зачем он в этом проекте.
ОтветитьУдалитьВот еслибы вы вторую половину доклада выдержали в томже ключе, что и первую - цены бы докладу небыло.
Т.к. я не единственных кто сказал вам об этом, надеюсь в будущем мы увидим доработанный доклад, я буду ждать его с нетерпением.
@Rusted
ОтветитьУдалитьСпасибо за совет. Этот я уже дорабатывать не буду, но возможно сделают отдельно про применение IoC контейнера.
Видео супер! Очень понравилось и подтолкнуло использовать принципы TDD.
ОтветитьУдалитьСпасибо, Александр! Чтобы прочувствовать все в полной мере я скачал ReSharper, xUnit и Ninject и параллельно с Вашим видео повторил все показанные действия. Очень впечатлило. Более того, я раньше как-то недооценивал ReSharper, а теперь не представляю себе жизни без него=)
ОтветитьУдалитьСкажите, а почему Вы выбрали NInject в качестве IoC контейнера? Я интересуюсь потому, что сам хочу выбрать для себя оптимальное решение.
@i.lukyanov
ОтветитьУдалитьРад, что понравилось =)
> Скажите, а почему Вы выбрали NInject в качестве IoC контейнера?
Я как раз сейчас готовлю видео про IoC-контейнер, там более подробно рассмотрю этот вопрос. Но сейчас заранее могу сказать, что не стоит использовать ни однин IoC-контейнер напрямую. Всегда надо делать над ним свою обертку. А Ninject мы использовали, потому что у него очень простой синтаксис, только и всего =)
Спасибо за видео! Еще раз убеждаюсь, что хороший скринкаст лучше многих, пусть даже хороших, статей.
ОтветитьУдалитьВозник вопрос: как быть, если не все классы сборки хочется делать видимыми извне? Только самое необходимое. Как тестировать в таком случае? Или это плохая практика и так делать не стоит?
@Constantin
ОтветитьУдалитьСпасибо за вопрос, он довольно актуальный.
Мы делаем такие классы internal и разрешаем тестовым сборкам видеть эти классы с помощью атрибута InternalsVisibleTo
Я пиши обычные прикладные приложения, в которых архитектура из трёх уровней не подходит. В программах много диалоговых окон и сценариев взаимодействия с пользователем. Пытаюсь облегчить себе работу написанием тестов. Но что то я ни как не могу увидеть выгоды от этого. Количество необходимых тестов быстро набигает за несколько десятков, для них приходится писать очень много кода. Да и как покрыть тестами взаимодействие пользователя с GUI?
ОтветитьУдалить@Алексей
ОтветитьУдалитьА вы на чем приложения пишите?
Может вам подойдет http://blog.byndyu.ru/2010/04/ioc-aspnet-mvp-model-view-presenter.html
Цитата: "А вы на чем приложения пишите?
ОтветитьУдалитьМожет вам подойдет http://blog.byndyu.ru/2010/04/ioc-aspnet-mvp-model-view-presenter.html"
Не очень. Я просто не понимаю где мне "зацепиться" чтоб дальше самому можно было разбираться.
@Алексей
ОтветитьУдалитьВсе-таки, на чем вы пишите приложения?
Что не понятного в моделе MVP?
Случайно нашел ваш блог. Спасибо за ваш труд. Очень кстати. Перечитываю все статьи и комменты.
ОтветитьУдалитьОтличное видео и отличные статьи. Очень помогли в изучении темы. Спасибо.
ОтветитьУдалитьОтличный вводный урок, спасибо.
ОтветитьУдалитьВопрос. В данном случае поведение еще несуществующего объекта задавалось через его интерфейс и мок-объект, который эмулировал поведение будущего объекта. Что если у нас есть некий язык, где нет никакой мок-подобной библиотеки и нет ничего похожего на лямбда-конструкции, можно ли будет создавать тесты? Если да, то как в таких случаях тестируется поведение объекта, сначала все же создается реальный объект?
@Sergey
ОтветитьУдалитьMoq я использовал только ради удобства. Вы можете взять и создать объект-заглушку вручную. Главное, чтобы он реализовывал нужный интерфейс. Просто Moq позволяет не писать это код.
Александр, скажи, а чем обусловлен твой выбор в пользу xUnit, а не скажем того что входит в VS?
ОтветитьУдалить@Ринат Муллаянов
ОтветитьУдалитьЛучшее API из всех предлагаемых.
Александр, расскажи, пожалуйста, каким образом ты интегрировал xUnit в MS Unit Testing Framework? В видео у тебя явно не консоль используется для запуска тестов. =)
ОтветитьУдалить@Юрий
ОтветитьУдалитьxUnit интегрирован в ReSharper с помощью http://xunitcontrib.codeplex.com
Александр, объясни в чем разница между BusinessLogic и Domain? Каковы критерии для разделения объектов по разным проектам?
ОтветитьУдалитьДомен - описание объектов предметной области и их поведение. Например, если вы создаете платформу для блогов, то это скорее всего будут: Пост, Комментарий, Контент, Реакция пользователя и т.п.
ОтветитьУдалитьБизнес-логика, по-другому можно назвать сервисы, туда входят команды и запросы, которые есть в вашем приложении. Команды - изменение состояния системы. Например, добавить новый комментарий. Запросы - выборки данных. Например, взять список комментариев для поста #4.