Самое лучшее объяснение из тех, что мне удалось посмотреть. Все понятно. Спасибо! Все отлично.
@Yarkendar8 жыл бұрын
Спасибо, это лучший урок по внедрению зависимостей. Было бы очень удобно видеть урок в виде статьи :)
@AlexS-gn9tq6 жыл бұрын
ВАУ!!!Спасибо огромное. Это настолько простое и наглядное объяснение! Я перечитал кучу разных статей но никак не мог въехать что это такое и зачем надо, а здесь всё так просто! Супер! Спасибо!
@СаняСанин-ш6у2 жыл бұрын
Спасибо, лучшее объяснение!
@NatuNuarat8 жыл бұрын
Сегодня на работе искала информацию по dependency injection и случайно набрела на Вашу статью на хабре. Узнала автора по аватарке XD Это было так приятно, как будто встретила старого знакомого! Спасибо за Ваши уроки! Они помогли мне освоить азы чтобы стать сейчас стажером-программистом =)
@_kul8792 жыл бұрын
Ве-ли-ко-ле-п-но! Хочу также про интеграционны тесты!
@sergepikovsky33858 жыл бұрын
30. Самое время подумать о классе который будет предоставлять данные. 1-ин класс - 1-а задача! ScriptGenerator - создает скрипт. Но он должен получать откуда то данные к этому скрипту. Для этого мы создали библиотеку TestDataGenerator.Data. В ней мы и разместим класс, предоставляющий данные нашему потребителю. Принято классы, которые работают с данными называть репозитариями. Но так как мы на стадии проектирования, то начинаем не с класса, а с интерфейса. Добавляем в библиотеку интерфейс IRepository.cs и делаем интерфейс открытым (public). 31. Возвращаемся к файлам, входящим в наше приложение. Они и являются опосредованным источником данных. Почему опосредованным? Из файла EnglishWord.txt мы не напрямую берем слова, а убираем номер строк, транскрипцию, толкование, а берем лишь вычлененное слово (слова). В какой то степени обработка коснется и остальных файлов. Т.е. наш класс-репозитарий длолжен обратится к этим файлам и предоставить потребителю информацию из них в удобном для потребителя формате. 32. Перечень этих данных четко виден в примере конечного скрипта. Помним, что почта = уникальный(!) логин + случайный ящик (ящик не запрашивается у файлов). Т.е. мы запрашиваем всего 4 сущности у репозитария - Имя, Отчество, Фамилию и логин. Все остальное - генерируем. Формируем методы для получения случайных данных из репозитария GetRandom...(); для всех 4-ех сущностей. Таким образом генератор скрипта будет абстрагирован от способа получения данных. 33. GetRandomUniqLogin() имеет одну тонкость: как определять границы сессии в течении которой формируются уникальные логины? Принимаем, что логины уникальны в течении генерации одного скрипта. Далее механизм учета уникальности сбрасывается и другой скрипт может однажды принять логин, который использовался, скажем, предыдущим скриптом. Для отработки этого механизма вводим метод инициализации репозитария void Init(); 34. Ну что? Пишем unit-test-ы на репозитарий? Нет!!! Unit-test покрывает не весь код. Даже вредно покрывать весь код! Юнит тестами покрывается основная часть кода - "Слой Бизнесс-Логики", т.е. библеотека .BL . Остальные слои не покрываем тестами исходя из идеологии unit-test-ирования. Unit-test-ами покрываются только независимые методы, которые ни к чему не привязаны. "Слой Призентации" привязана либо к файловой системе (наш случай), либо к UI, либо к приему пользовательских запросов, т.е. привязан к "Внешней Среде", которую мы не можем контролировать в unit-test-ах. Нижний слой - "Слой Доступа к данным" тоже зависит, но уже от "Источника[Хранилище] Данных" (папка Files). Это может быть и БД и т.д. Именно по этому интерфейс IRepository останется не покрытым unit-test-ированием. 35. Теперь подходим к реализации. Создаем открытый класс Repositary. 36. Наследоваться этот класс должен от IRepositary. Resharper на автомате реализует все поведение с реализацией throw new NotImplementedException(); Это очень хорошое исключение. Всегда стоит применять это исключение у нереализованых методов. Ни каких null - это приведет к серьезным ошибкам! Итак, все методы исполняют throw new NotImplementedException(); На данном этапе нас такая реализация устраивает. 37. Возвращаемся к интерфейсу IScriptGenerator и для него делаем все тоже самое, что и на предыдущих двух шагах для IRepositary. Т.е. создаем наследуемый от IScriptGenerator класс ScriptGenerator и просим Resharper сгенерировать все интерфейсные методы с заглушкой - throw new NotImplementedException(); 38. Псевдореализация интерфейсных методов (а других и нет) класса ScriptGenerator готова. Теперь в тестах мы можем заглушку объектов из null преобразовать в реальные объекты: public void Init() public void Init() { { _generator = null; -> _generator = new ScriptGenerator(); } } 39. Запускаем тестирование и убеждаемся в том, что все тесты упадут (что хорошо). 40. Возвращаемся в ScriptGenerator и приступаем к реализации: - public UserEntity GenerateUser() Код тривиальный, вся логика метода в этой сроке: entity.Email = string.Format("{0}@{1}", entity.Login, randomEmailDomain); 41. Пробуем запустить тесты и получаем сообщение о не реализации репозитария. Здесь есть тонкий момент, который важно понимать: - мы тестируем не класс репозитария, - мы тестируем ScriptGenerator, т.е. логику. - нам совершенно не важно как себя ведет класс репозитария. Но как мы можем абстрогироваться от класса репозитария если мы в нашем методе постоянно к нему обращаемся??? В 99% случаев методы одного класса будут зависеть от методов другого класса, но идеология unit-tseting заключается в том, что тестируется только код одного метода. Без зависимости от другого (третьего) метода. В этом случае разработчики прибегают к приему, которая называемся иньекция зависимости (Dependency Injection). В чем он заключается (практически)? Возвращаемся в метод GenerateUser(), конкретно его первую строку: IRepository repository = new Repositary(); - это очень "нехорошая" строка кода, т.к. она гвоздями прибивает наш метод к классу репозитария, в частности к его реализации {new Repository}. Если мы захотим заменить, то мы будем должны найти в классе ScriptGenerator все методы, в которых используется репозитарий и там его заменить на какую-то другую переменную. Это очень не удобно! 42. В таких то случаях и делают инъекцию зависимостей, которая к тому же явно показывает от каких именно классов зависит наш класс. Инъекция может быть проброшена в класс многими способами, но мы рассмотрим самый очевидный - Инъекцию Через Конструктор: a) Создаем закрытое поле для чтения типа IRepositary - private readonly IRepository _repository; б) Создаем конструктор нашего класса в котором инициируем созданое поле передаваемым в конструктор параметром. в) Заменяем все ссылки на репозитарий на ссылку на вновь созданное поле. г) Коментируем (убираем) строку создания старого экземпляра репозитария. 43. Что мы получили? Наш класс полностью избавился от зависимости от репозитария. Он по прежнему обращается к объекту репозитария, но теперь зависимость от объекта репозитария поступает от конструктора класса. Т.е управление этими зависимостями возлагается на иной класс. А именно на управляющий класс по отношению к ScriptGenerator (инстанцирующий ScriptGenerator). Для изменения зависимости ему хватит поменять параметр в конструкторе. А поскольку в качестве типа параметра мы указываем не конкретный класс, а интерфейс, то мы можем сюда подставить любой класс который реализует интерфейс параметра конструктора. А таких классов может быть уже достаточно много. Теперь можно переходить к тестовому классу!
@Milording8 жыл бұрын
Очень круто. Жду продолжения и надеюсь, будут темы с IoC и IoC-контейнерами, а то пока это все в голове слабо связывается с DI
@BoxaShu8 жыл бұрын
Спасибо. жду продолжения.
@anatoliymedinets2416 жыл бұрын
Однозначно лайк!
@denyszorin86758 жыл бұрын
Здравствуйте, у меня есть вопрос Как правильно нужно приплюсовывать стринги ?? на собеседовании мне сказали что вот так делать нежелательно - "some string" + " new string"
@Defazze8 жыл бұрын
+Денис Зорин через string.format или (в C# 6) через интерполяцию строк
@denyszorin86758 жыл бұрын
+Программирование - это просто , нашел ответ на свой вопрос в вашем следующем уроке. Как вариант еще это при помощи стринг билдера ) спасибо за ответ )
@qwertyq25708 жыл бұрын
+Денис Зорин Странно, что в этом такого ? Надо им Рихтера дать почитать. ) "Чтобы объединить несколько строк в одну строку, используйте оператор + языка C#: String s = "Hi" + " " + "there."; // Конкатенация трех литеральных строк образует одну литеральную строку Поскольку все строки в этом коде литеральные, компилятор выполняет их конкатенацию на этапе компиляции, в результате в метаданных модуля оказывается лишь строка "Hi there.". Конкатенация нелитеральных строк с помощью оператора + происходит на этапе выполнения. Для конкатенации нескольких строк на этапе выполнения оператор + применять нежелательно, так как он создает в куче несколько строковых объектов. Вместо него рекомендуется использовать тип System.Text.StringBuilder"
@NikitaReznikow8 жыл бұрын
Спасибо за уроки, давно хотел учить тесты, сильно помогли! Такой ворос: у меня Password_Required проходит, я так понимаю что это связано с тем что, проверяет на Empty а не на Null, ибо когда меняю проверку на null тест не проходит, хотя полностью повторил ваш код?
@WorldCount8 жыл бұрын
Вот и я это заметил. Причем если в Mock изменить возвращаемое значение для имени на null, то начинает проходить тест и для имени. Походу везде надо проверять именно на null.
@WorldCount8 жыл бұрын
Вообще не пойму, в коде у автора везде на Empty проверка и у него не проходит.
@svLimones8 жыл бұрын
А будет ли показаны другие типы тестов? Авто-тесты, функциональные тесты?
@Defazze8 жыл бұрын
+Arthur “svLimones” Ishmatov В рамках этого курса - скорее всего нет
@jestemzbiaorusi83794 жыл бұрын
Мне кажется до меня дошло... :)
@ТуралИскендерли2 жыл бұрын
Очень хороший урок👍 Сегодня провалился на Внедрении зависимостей на собеседовании. А теперь смотрю оказывается я им пользовался довольно таки часто не зная ему названия😄. Только опять таки что то не оговаривается. Или я не понимаю. Зачем нам поменять класс Repository если другой класс тоже все равно обязан реализовать тот же интерфейс и те же методы? И следовательно зачем депенденси инжекшн в таком случае?
@sergepikovsky33858 жыл бұрын
Прошу прощения если сделал не правильно, просто конспект всегда пишу, чтобы помнил не только мозг, но и 10 пальцев, да и вспоминать легче... может пригодиться кому.
@dimabogdan13808 жыл бұрын
+Serge Pikovsky так же делаю, отлично помогает, только конспектировать правильно нужно)
@sergepikovsky33858 жыл бұрын
+Dima Bogdan Интересно, что по вашему правильно? У меня особых правил нет. Ну, не совсем как акын, конечно, пишу, что слышу... стараюсь придать какую то структуру записям и если решил писать не выборочно, то стараюсь не пропускать смысловых блоков лекции/урока. Есть правила Димы Богдана? :-)
@sergepikovsky33858 жыл бұрын
+Dima Bogdan А, еще... когда я здесь написал "правильно", я имел ввиду без разрешения читающего лекцию.
@sanyasa20928 жыл бұрын
Неплохо бы уроки для Windows (UWP) . Мне например уже заказывают для девайсов на разбери.
@Milording8 жыл бұрын
+sanya sa я попробую сделать уроки, Игорь посмотрит. Если подойдут, то ожидайте! :)