практически возможно ли сделать хороший TDD (или BDD) без использования какой-либо структуры DI в java?

IMO одна из основных характеристик хорошего TDD: тестирование вашего класса (или фактически модуля) изолированно.

Когда вы это сделаете, вы сможете фактически протестировать отдельное поведение в каждом тесте - только один тест подойдет для одной вашей проблемы.

Для этого вы сначала должны убедиться, что в вашем классе нет статических ссылок (включая конструкторы AKA new ключевое слово).

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

Я обнаружил, что эту теоретическую МО слишком сложно реализовать на практике.

Я упустил что-то важное в процессе?

РЕДАКТИРОВАТЬ: я действительно думаю, что мы все должны стремиться к одному провалу за одно изменение кода. он никогда не будет идеальным, но он приблизит ваш код к этому, люди забывают, что тестовый код также является поддерживаемым кодом, спецификацией проверенных изменений кода.

Если вы не нацеливаетесь туда, вы в конечном итоге удалите тесты, что является плохим решением.


person ekeren    schedule 16.09.2010    source источник


Ответы (3)


Я не думаю, что вам нужны фреймворки DI для модульного тестирования - TDD существовала за много лет до того, как DI стало модой. А в случае кода, который сам не использует явную структуру DI, зачем вам использовать это для модульных тестов?

Конечно, фреймворки DI могут в некоторой степени упростить задачу. OTOH они делают это, помещая сложность в другое место. Я предпочитаю иметь автономные модульные тесты, и до сих пор я почти всегда обходился без инфраструктуры DI. Это часто требует рефакторинга моего тестового кода, а иногда даже его дублирования, но я могу с этим жить. И, как отметила @kostja, насмешливые фреймворки тоже очень помогают.

Проектирование с учетом тестируемости также важно - если класс сложно протестировать как таковой, лучше провести его рефакторинг, чем пытаться облегчить боль с помощью инфраструктуры DI.

Обратите внимание, что все это требует большой практики, а также изменения мышления и образа мышления. Все это требует времени и терпения ... чем больше вы успеваете, тем лучше становитесь :-)

вы можете фактически протестировать отдельное поведение в каждом тесте - только один тест подойдет для одной вашей проблемы.

Я думаю, что ваш вывод здесь неверен. Даже если вы тестируете что-то одно в каждом тесте, вы можете сломать множество тестов с помощью одного изменения кода. Обновление: каждый тест предназначен для проверки единственного пути выполнения в тестируемом коде. Однако эти пути выполнения редко бывают независимыми - обычно есть общие разделы пути во всех, кроме простейших методов. Любое изменение может нарушить несколько тестов.

person Péter Török    schedule 16.09.2010
comment
Спасибо за ответ и редактирование. Я использую mocking allot, чтобы изолировать то, что я тестирую, я думаю, мы все должны стремиться к единственному провалу теста изменения кода, конечно, это невозможно. Я вставляю ли вы свои зависимости в конструктор или у вас есть новые внутри кода, не относящегося к фабрикам? - person ekeren; 16.09.2010
comment
@ekeren, я работаю с устаревшим кодом, поэтому разрешено все :-), если это помогает писать модульные тесты. Я делаю DI через конструкторы, специальные сеттеры, даже через методы подкласса, переопределенные специально для модульного тестирования. Подробнее см. Эффективная работа с устаревшим кодом. . - person Péter Török; 16.09.2010
comment
+1 за возражение против вывода о том, что только один тест не пройден для данной проблемы. - person jvenema; 16.09.2010
comment
@ekeren, единичный сбой теста для единственного изменения кода действительно нереалистичен - см. расширенное объяснение в моем ответе. - person Péter Török; 16.09.2010
comment
Моя библия - "Эффективная работа с устаревшим кодом" (наряду с "Эффективной Java" :)). Мне не нравится делать переопределения, когда я могу вводить зависимости в конструкторы, поэтому, когда я создаю новый код, я пытаюсь переместить свой DI в конструкторы, спасибо за ваш ответ. Я действительно думаю, что мы все должны стремиться (а люди редко это делают) к одному провал за одно изменение. это никогда не будет идеальным - person ekeren; 16.09.2010
comment
@ekeren, конечно, переопределения и подобные неприятные уловки - это последнее средство. Но с устаревшим кодом мне иногда это нужно. Что касается сбоев при каждом изменении, я думаю, что это не совсем актуально - все мы стремимся к тому, чтобы наши модульные тесты были на 100% экологичными, не так ли? Так что мне все равно, сколько из моих модульных тестов выйдет из строя, если есть ошибка - я просто иду и исправляю ее, а затем снова наслаждаюсь зеленым светом :-) - person Péter Török; 16.09.2010
comment
@Peter - проблема с выделением теста, приходящимся на одну проблему, заключается не в том, что у вас есть ошибка, а в том, когда изменяется поведение, тогда вам нужно исправлять тесты ... а не код. сколько тестов вам нужно будет исправить - вот в чем проблема. - person ekeren; 16.02.2011
comment
@ekeren, это зависит от того, насколько детализированы ваши модульные тесты. Если у вас есть небольшие и легко тестируемые методы, вы можете хорошо сфокусировать свои модульные тесты, поэтому у вас будет меньше тестовых примеров, выполняющих какую-либо конкретную строку кода. Но все равно редко будет 1. Исправить 2-3 теста вместо 1 меня устраивает, а 400 однозначно неприемлемо :-) - person Péter Török; 16.02.2011

Предполагается, что DI сокращает прямые зависимости, фактически упрощая имитацию и модульное тестирование. Может, вам не хватает фиктивной части? этот пост может прояснить ситуацию.

person kostja    schedule 16.09.2010
comment
Спасибо за Ваш ответ. Я использую mocking allot, я не думаю, что вы можете сделать хороший TDD без использования mocks или, по крайней мере, придумывая свои собственные mocks - person ekeren; 16.09.2010

Конечно, это возможно. И легко. Например, предположим, что вы хотите протестировать следующий метод, который вызывает статические методы класса Database (статический фасад постоянства) и напрямую создает экземпляр SimpleEmail (из электронной почты Apache Commons):

public void doBusinessOperationXyz(EntityX data) throws EmailException
{
   List<EntityX> items = 
      Database.find("select x from EntityX x where x.xyz=?", data.getXyz());

   BigDecimal total = ...compute/obtain the value...
   data.setTotal(total);

   Database.persist(data);

   Email email = new SimpleEmail();
   email.setSubject("Notification about processing of ...");
   email.addTo(data.getCustomerEmail());
   email.setMsg("...some notification message...");
   email.send();
}

Этот метод можно протестировать отдельно от зависимостей Database и Email с помощью следующего модульного теста, используя JMockit. :

@Test
public void doBusinessOperationXyz() throws Exception
{
   final EntityX data = new EntityX(5, "abc", "5453-1");
   final List<EntityX> items = new ArrayList();
   items.add(new EntityX(1, "AX5", "[email protected]"));

   new Expectations()
   {
      @Mocked final Database unused = null;
      @NonStrict SimpleEmail email;

      {
         Database.find(withSubstring("select"), (Object[]) any); result = items;
         Database.persist(data);

         email.send(); times = 1;
      }
   };

   new MyBusinessService().doBusinessOperationXyz(data);
}

Обычно необходимо внедрять фиктивные объекты для зависимостей Database и Email, что приводит к более сложному решению. Имитируя / заменяя статические методы и конструкторы, мы можем вместо этого сохранить простой, естественный и элегантный дизайн.

person Rogério    schedule 24.11.2010