• Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо

      Многие программисты при выборе между интеграционным и юнит-тестом отдают предпочтение юнит-тесту (или, иными словами, модульному тесту). Некоторые считают интеграционные тесты антипаттерном, некоторые просто следуют модным тенденциям. Но давайте посмотрим, к чему это приводит. Для реализации юнит-теста mock-объекты навешиваются не только на внешние сервисы и хранилища данных, но и на классы, реализованные непосредственно внутри программы. При этом, если мокируемый класс используется в нескольких других классах, то и mock-объект будет содержаться в тестах на несколько классов. А поскольку тестируемое поведение принято задавать внутри теста (смотри given-when-then, arrange-act-assert, test builder), то поведение моки каждый раз заново задаётся в каждом тесте, и нарушается принцип DRY (хотя дублирования кода может и не быть). Кроме того, поведение класса декларируется в mock-объекте, но сама эта декларация не проверяется, поэтому со временем задекларированное в моке поведение может устареть и начать отличаться от реального поведения мокируемого класса. Это вызывает целый ряд сложностей:

      1)Во-первых, при изменении функционала сложно вообще вспомнить, что помимо класса и тестов на него нужно изменить ещё и моки этого класса. Давайте рассмотрим цикл разработки в рамках TDD: «создание\изменение тестов на функционал -> создание\изменение функционала -> рефакторинг». Mock-объекты являются декларированием поведения класса и не имеют отношения ни к одной из этих трёх категорий (не являются тестами на функционал, несмотря на то, что в тестах используются, и уж тем более не являются самим функционалом). Таким образом, изменение mock-объектов классов, реализованных внутри программы, не укладывается в концепцию TDD.

      2)Во-вторых, сложно найти все места мокирования этого класса. Я не встречал ни одного инструмента для этого. Тут можно или написать свой велосипед, или смотреть все места использования этого класса и отбирать те, где создаются моки. Но при неавтоматизированном поиске можно и ошибиться, проглядеть что-нибудь. Тут у вас, наверное возник вопрос: если проблема столь фундаментальна, как описывает автор, неужели никому не пришло в голову реализовать инструменты, упрощающие её решение? У меня есть гипотеза на этот счёт. Несколько лет назад я начал писать библиотеку, которая должна была собирать mock-объект так же, как IOC-контейнер собирает обычный класс, и автоматически создавать и прогонять тесты на поведение, описываемое в моках. Но затем я отказался от этой идеи, потому что нашёл более элегантное решение проблемы моков: просто не создавать эту проблему. Вероятно, по схожей причине специализированный инструмент для поиска моков конкретного класса или не реализован, или малоизвестен.

      3)В-третьих, мест мокирования класса может быть много, и изменение их всех — рутинное занятие. Если программист вынужден делать рутину, которую невозможно автоматизировать, то это явный признак того, что с инструментами, архитектурой или рабочими процессами что-то не в порядке.

      Надеюсь, суть проблемы ясна. Далее я опишу пути решения этой проблемы и расскажу, почему, с моей точки зрения, интеграционные тесты предпочтительнее юнит-тестов.


      Читать дальше →
    • Вышла новая версия LinqTestable — библиотеки для тестирования запросов к бд через ORM

        LinqTestable — это библиотека, помогающая преодолеть в тестах концептуальный разрыв между ООП и реляционной БД, возникающий из-за разницы поведения NULL-а в этих двух парадигмах. Например, сравнение NULL == NULL возвращает истину в объектных языках, и ложь в реляционной модели. Помимо этого, NULL.SomeField вернёт NULL в реляционной модели и выбросит NullReferenceException в C#. LinqTestable предназначена для решения этой проблемы.


        Читать дальше →
      • Кастанедовский воин на поприще управления рисками

          Хотите узнать что такое управление рисками и как с ними справляться с ловкостью ниндзи?
          Тогда добро пожаловать под кат!

          (В этой статье изложены мысли, навеянные творчеством Тома ДеМарко, и скорее всего статья будет неинтересна тем, кто уже знаком с его произведениями)


          Читать дальше →
        • Осторожно, истинные контракты классов могут отличаться от формальных

            Вкратце, в этой статье речь пойдёт о правиле наследования Лисков, о различии контрактов NotifyCollectionChangedAction.Reset в версиях .NET Framework 4 и .NET Framework 4.5, и о том, какой из этих двух контрактов истинный, а какой — ошибочный.


            Читать дальше →
          • Ещё один способ реализации binding-а вычислимых свойств в WPF

              Допустим, есть проект на WPF и в нём ViewModel, в которой есть два свойства Price и Quantity, и вычислимое свойство TotalPrice=Price*Quantity

              Код
              public class Order : BaseViewModel
                  {
                      private double _price;
                      private double _quantity;
              
                      public double Price 
                      {
                          get { return _price; }
                          set
                          {
                              if (_price == value)
                                  return;
                              _price = value;
                              RaisePropertyChanged("Price");
                          }
                      }
              
                      public double Quantity
                      {
                          get { return _quantity; }
                          set
                          {
                              if (_quantity == value)
                                  return;
                              _quantity = value;
                              RaisePropertyChanged("Quantity");
                          }
                      }
              
                      public double TotalPrice {get { return Price*Quantity; }}
                  }
              
                  public class BaseViewModel : INotifyPropertyChanged
                  {
                      public event PropertyChangedEventHandler PropertyChanged;
              
                      protected virtual void RaisePropertyChanged(string propertyName)
                      {
                          var propertyChanged = PropertyChanged;
                          if (propertyChanged != null)
                              propertyChanged(this, new PropertyChangedEventArgs(propertyName));
                      }
                  }
              




              Если Price будет изменен в коде, то изменения цены автоматически отобразятся в View, потому что ViewModel сообщит View об изменении Price посредством вызовом события RaisePropertyChanged(«Price»). Вычисляемое TotalPrice же не изменится в View, потому что никто не вызывает RaisePropertyChanged(«TotalPrice»). Можно вызывать RaisePropertyChanged(«TotalPrice») в тех же местах, где вызывается RaisePropertyChanged(«Price») и RaisePropertyChanged(«Quantity»), но не хотелось бы размазывать по множеству мест информацию о том, что TotalPrice зависит от Price и Quantity, а хотелось бы хранить информацию об этом в одном месте. С этой целью люди пишут разнообразные менеджеры зависимостей, но давайте посмотрим какой минимальный код на самом деле нужен для этого.
              Читать дальше →
            • Библиотека, помогающая преодолеть концептуальный разрыв между ООП и БД во время тестирования при использовании ORM, — LinqTestable

                Как известно, между объектно-ориентированной и реляционной моделью существует концептуальный разрыв, преодолеть который не в состоянии даже ORM. В основном этот разрыв влияет на то, что при использовании реляционной базы данных мы вынуждены работать над множествами, а не над конкретными объектами. Но есть и другой фактор: поведение NULL в бд отличается от поведения NULL в объектно-ориентированных языках. Это может стать проблемой, когда вы используете один и тот же запрос в двух ситуациях: 1) при запросе к бд 2) при юнит-тестировании, когда вместо таблицы из бд используется массив в оперативной памяти. Более того, это может стать проблемой, если вы обращаетесь только к бд, но мыслите о NULL в терминах ООП, а не реляционной бд!

                image
                Читать дальше →
              • Материя состоит из пустоты или 0,(9)=1

                  Быть может, эти электроны
                  Миры, где пять материков,
                  Искусства, знанья, войны, троны
                  И память сорока веков!
                  Ещё, быть может, каждый атом — Вселенная, где сто планет;
                  Там — всё, что здесь, в объёме сжатом,
                  Но также то, чего здесь нет.
                  Брюсов В.Я.

                  Согласно теории бесконечной вложенности материи, атом можно бесконечно дробить на более мелкие части. Попробуем вычислить размер, энергию или любую другую произвольную метрику этой самой мелкой части. Скорее всего, вы ожидаете, что вычислить конкретное значение этой самой мелкой части не удастся, и в ответе получится предел, но я попытаюсь показать, что это не так.

                  image

                  Спойлер
                  Эта статья — розыгрыш, или, правильнее сказать, головоломка. Я предлагаю читателю найти то место, в котором допущена логическая ошибка. В статье используется математика 9 класса средней школы.

                  Читать дальше →
                • Пролог – декларативный язык, способный решать любые ребусы и доказывать теоремы

                    Представьте себе высокоуровневый язык, в котором не нужно указывать КАК получить результат, вместо этого нужно просто указать ЧТО вы хотите получить. При этом область применения языка не ограничена и язык способен решать те же задачи, что и любой другой высокоуровневый язык, наподобие JAVA. Кажется фантастикой, не правда ли? Однако такой язык есть и называется он PROLOG. Посмотрим как PROLOG справляется с этой задачей на примере загадывания прологу некоторых загадок и попросим PROLOG выдать доказательство теоремы.

                    image

                    Читать дальше →
                  • KISS — принцип проектирования, содержащий все остальные принципы проектирования

                      Постараюсь объяснить сущность принципа проектирования KISS просто и одновременно очень подробно. KISS – это очень общий и абстрактный принцип проектирования, который содержит в себе практически все остальные принципы проектирования. Принципы проектирования описывают как писать «хороший» код. Однако что значит хороший код? Некоторые считают, что это код, который выполняется максимально быстро, некоторые – что это код, в котором задействовано как можно больше паттернов проектирования… Но верный ответ лежит на поверхности. Код – это информация в чистом виде. А основные критерии ценности информации – это 1)достоверность 2)доступность 3)понятность. То, почему важны достоверностью и доступность – очевидно. От кода нет проку, если он работает с ошибками или если сервер с приложением «лежит». Почему же важна понятность кода? В понятном коде проще искать ошибки, проще его изменять, дорабатывать и сопровождать. Итак, понятность – основная ценность, к которой должен стремиться программист. Однако тут есть одна неувязочка. Дело в том, что понятность – вещь сугубо субъективная.
                      Читать дальше →
                    • Монолитная система VS множество самостоятельных модулей на примере притчи о слоне и мудрецах

                        Данная статья представляет собой в основном вольный пересказ фрагмента книги Эрика Эванса «Предметно-ориентированное проектирование», в котором он рассуждает о Separate Ways и Bounded Context.

                        Допустим, есть следующая ситуация: несколько групп разработчиков работают над одним проектом, но решают разные задачи. Например, проект представляет собой конструктор микросхем, и первая команда реализует функциональность прозванивания микросхемы, а вторая – расчётом себестоимости микросхемы. Вопрос: как лучше поступить – 1)позволить обеим командам «вариться в собственном соку», получив на выходе два модуля, код которых практически нигде не пересекается, или же 2)наладить коммуникацию между двумя командами, заставить их работать сообща и получить на выходе единую монолитную систему? На этот вопрос не существует универсального ответа («да» или «нет») и найти ответ в этой ситуации нам поможет аналогия со слоном и мудрецами.


                        Читать дальше →
                      • Миф о идеальном количестве строк в методе

                        Существует миф, что если в функции больше чем n или меньше чем m строк кода, то с функцией есть проблемы в плане проектирования. К примеру, автор публикации «Размышления о принципах проектирования» говорит, что «число строк метода… является необходимым, но недостаточным условием хорошего дизайна». В этой статье я изложу причины, по которым считаю необходимость функции быть определенного размера мифом и приведу примеры, доказывающие это, но сначала давайте рассмотрим причины популярности этого мифа.
                        Читать дальше →