Как написать модульный тест, издеваясь, когда у вас нет аргументов: конструкторы

Я пытался написать модульный тест, используя jmocks и junit. (Мой проект использует ядро ​​java - без фреймворков -) Я не мог написать модульный тест для некоторых из моих классов, издеваясь над внешними зависимостями, когда зависимости были инициализированы в конструкторе без аргументов.

Поскольку я не могу предоставить реальный код, пытаюсь объяснить сценарий на примере.

public interface Apple {

String variety();

}

Выполнение.

public class MalgovaApple implements Apple {

  @Override
  public String variety() {
         return "Malgova";

  }

}

Класс для тестирования

public class VarietyChecker {
private Apple apple;

VarietyChecker(){
this.apple = new MalgovaApple();
// instead of new, a factory method is used in actual application
}

public String printAppleVariety(){
    String variety = apple.variety();
    if(variety.length() < 3){
       System.out.println("Donot use Code names- Use complete names");
       return "bad";
        }
       return "good";
}
}

Тест Junit с использованием jmock

public class VarietyCheckerUnitTest{
Mockery context = new JUnit4Mockery();
@Before
public void setUp() throws Exception {
}

@After
public void tearDown() throws Exception {
}

@Test
public void test_VarietyChecker() throws Exception{

    final Apple mockapple = context.mock(Apple.class);

    VarietyChecker printer = new VarietyChecker();
    context.checking(new Expectations(){{
        oneOf(mockapple).variety();will(returnValue("as"));
    }});
    String varietyNameValid = printer.printAppleVariety();

    assertEquals("bad",varietyNameValid);


} }

Этот тест не проходит - Mocking не работает, значения "as" не вводятся, тестовый класс выполняется с помощью MalgovaApple...

Теперь, если мы добавим приведенный ниже конструктор в VarietyChecker и используем его тестовый пример, он даст ожидаемый результат...

public VarietyChecker(Apple apple) {
    super();
    this.apple = apple;
}

и в модульном тесте создайте объект тестового класса, например VarietyChecker.printer = new VarietyChecker(mockapple);

Выставлять новый конструктор только для целей тестирования — не лучшая идея. В конце концов, сказано, что вы не должны изменять код только для тестирования, более того, я боюсь, что мы уже написали «некоторое» (количество) кода...

Я что-то упустил в junit или jmock, что может заставить работать насмешки даже в случае конструкторов без аргументов. Или это ограничение простого junit и jmocks, и я должен перейти на что-то мощное, например Jmockit /PowerMock?


person searchingforPerfection    schedule 05.06.2015    source источник
comment
Если вам нужно изменить код, чтобы иметь возможность его протестировать, код написан не очень хорошо. Слишком много жестких зависимостей от других вещей, чтобы это работало хорошо. Тем не менее, я не могу сказать, что массовый переход на Mockito решит ваши проблемы; это один из многих, я уверен.   -  person Makoto    schedule 06.06.2015
comment
@Makoto Я возражаю против твоего первого предложения. Это мантра, сформулированная приверженцами юнит-тестирования, которая препятствует честной оценке ценности теста. Раньше я сам в это верил, но это было до того, как я понял, что Серебряных Пулей не существует. Реальность такова, что написание хороших тестов и понимание обратной связи, которую они вам дают, — это такой же навык и искусство, как и написание хорошего кода, и это навык, которым также трудно овладеть.   -  person jpmc26    schedule 06.06.2015


Ответы (1)


Вы должны рассмотреть два варианта.

  1. Используйте параметр конструктора, как вы описываете.

    В этом случае вы не «открываете новый конструктор только для тестирования». Вы делаете свой класс более гибким, позволяя вызывающим объектам использовать другую реализацию фабрики.

  2. Не издевайтесь над этим.

    В этом случае вы заявляете, что никогда не имеет смысла использовать другую фабрику. Иногда это нормально. Однако в этот момент вопрос меняется. Вместо «Как мне издеваться над этим?» теперь ваш вопрос звучит так: «Что я получу, написав этот тест?» Возможно, вы ничего не получите, и может вообще не иметь особого смысла писать тест.

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

    Важно не поддаваться менталитету «модульное тестирование всего». Это рецепт повреждения конструкции, вызванного испытаниями. Оценивайте свои тесты в каждом конкретном случае, решая, приносят ли они вам реальную пользу или нет. Отказ от написания модульного теста — допустимый вариант, а иногда даже уместный, даже если вы изо всех сил стараетесь этого избежать.

Только вы можете определить, какой из них имеет наибольший смысл в данном случае. Учитывая тот факт, что речь идет о фабричном объекте, я, вероятно, склоняюсь к первому.

person jpmc26    schedule 05.06.2015
comment
Существует третий вариант (хотя и не самый лучший): использовать отражение в модульном тесте для динамической установки переменной-члена apple в VarietyChecker. - person neuronaut; 06.06.2015
comment
@neuronaut Я думал о существовании таких хаков, когда писал, но не мог придумать лучшего способа представить это. Однако вы вдохновили на небольшую модификацию; Надеюсь, вы одобрите. ;) - person jpmc26; 06.06.2015
comment
Я не уверен, насколько ясен мой пример для реального случая. На самом деле мой VarietyChecker - это класс веб-службы Джерси, а конструктор no: arg загружает некоторые файлы свойств... Конструктор аргументов бесполезен, кроме как в тестовых потоках. @jpmc26 ваш второй пункт хороший ... я был в тестовом режиме всего ... но мне все еще нужно проверить в некоторых случаях - person searchingforPerfection; 06.06.2015
comment
@searchingforPerfection Я думаю, в этом случае вам следует издеваться. Разрешая передавать фабрику, вы упрощаете свой класс и повышаете гибкость. Имеет смысл избегать привязки класса к конкретному загрузчику конфигурации. Это хороший способ изолировать остальной код от внешних систем (файловая система, база данных, внешний веб-сервис). Однако я бы, вероятно, не проводил модульное тестирование внутренних компонентов самого загрузчика конфигурации. - person jpmc26; 06.06.2015
comment
@searchingforPerfection На самом деле, вместо макета, я мог бы выступить здесь за заглушку. Вы не пытаетесь проверить правильность вызова загрузчика конфигурации. Вы пытаетесь проверить, правильно ли ведет себя ваш класс с учетом определенных данных из загрузчика конфигурации. Поэтому, хотя я создал бы аргумент конструктора, я не стал бы утверждать объект фабрики, который вы передаете. вы используете их, imo, чем в реализации.Так что вам не обязательно нужны новые инструменты для этого.) - person jpmc26; 06.06.2015