Как проверить окончательные и статические методы проекта утилиты?
Я пытаюсь выполнить модульное тестирование для aproject, он использует устаревший проект "утилита", который усеян статическими методами, и многие из классов являются окончательными или их методы являются окончательными. Я вообще не могу обновить старый проект.
JMock и EasyMock оба задувают окончательные методы, и я не вижу приятного способа протестировать статические вызовы. Какие существуют методы тестирования?
Ответы
Ответ 1
Если вы можете реорганизовать свой код, вы можете привязать свои вызовы к окончательным/статическим методам в простых методах экземпляра, например:
protected Foo doBar(String name) {
return Utility.doBar(name);
}
Это позволяет вам переопределить метод оболочки в unit test, чтобы вернуть экземпляр foo.
В качестве альтернативы вы можете использовать Powermock, который расширяет Easymock (и Mockito), чтобы позволить насмехаться над финальными и статическими методами:
PowerMock - это платформа, которая расширяет другие макетные библиотеки, такие как EasyMock, с более мощными возможностями. PowerMock использует настраиваемую загрузку классов и байт-кода, чтобы включить издевательство над статическими методами, конструкторами, конечными классами и методами, частными методами, удалением статических инициализаторов и т.д.
Здесь пример тестирует насмешливый статический окончательный метод, пример показывает, как издеваться над другими типами:
@Test
public void testMockStaticFinal() throws Exception {
mockStatic(StaticService.class);
String expected = "Hello altered World";
expect(StaticService.sayFinal("hello")).andReturn("Hello altered World");
replay(StaticService.class);
String actual = StaticService.sayFinal("hello");
verify(StaticService.class);
assertEquals("Expected and actual did not match", expected, actual);
// Singleton still be mocked by now.
try {
StaticService.sayFinal("world");
fail("Should throw AssertionError!");
} catch (AssertionError e) {
assertEquals("\n Unexpected method call sayFinal(\"world\"):",
e.getMessage());
}
}
Ответ 2
Как насчет уровня косвенности/инъекции зависимостей?
Поскольку проект устаревшей утилиты - это ваша зависимость, создайте интерфейс, чтобы отделить его от вашего кода. Теперь ваша реальная/производственная реализация этого интерфейса делегирует устаревшие утилиты.
public LegacyActions : ILegacyActions
{
public void SomeMethod() { // delegates to final/static legacy utility method }
}
Для ваших тестов вы можете создать макет этого интерфейса и избежать взаимодействия с устаревшей утилитой.
Ответ 3
JMockit позволяет вам высмеивать статические методы и финальные классы. Я предполагаю, что он использует некоторый classloadin-fu, хотя я действительно не изучал это.
JMockit Expectations API позволяет устанавливать ожидания для любого вида вызова метода (для интерфейсов, абстрактных классов, конкретных конечных или не конечных классов и для статических методов), а также для создания экземпляров классов с помощью любых конструкторов.
Ответ 4
Как уже указывалось, JMockit можно использовать. Пример:
@Test
public void mockStaticAndFinalMethods(@Mocked LegacyService mock) {
new Expectations() {{
LegacyService.staticMethod("hello"); result = "Hello altered World";
}};
String actual = LegacyService.staticMethod("hello");
new LegacyService().finalMethod(123, "test");
assertEquals("Hello altered World", actual);
new Verifications() {{
mock.finalMethod(123, "test"); // verify this call occurred at least once
}};
}
Ответ 5
Если ваш метод без рефакторинга использует что-то вроде JNDI для подключения к другой службе, я бы подумал о запуске службы JDNI и заполнении ее с помощью заглушек, которыми вы управляете. Это боль, но относительно простая. Это может означать настройку базы данных или прослушивателя JMS или что-то еще, но должна быть легкая реализация Java, которую вы можете опробовать в тестах.
Ответ 6
JMock вместе с JDave может издеваться над окончательными методами и классами, если вам нужно. Здесь
являются инструкциями. При этом я бы рассматривал этот устаревший код (как это уже предполагали другие) в качестве внешних зависимостей и построения интерфейсов и имитировал их. Это еще один слой косвенности, но поскольку вы не можете изменить этот старый код, он кажется разумным.