#java #unit-testing #powermock #powermockito
#java #модульное тестирование #powermock #powermockito
Вопрос:
Я хотел бы знать, есть ли способ проверить и вызвать макет, созданный для частного статического метода, вызываемого из тестируемого общедоступного статического метода.
Вот мой тестируемый общедоступный статический метод
public static String methodUnderTest(String p1){
return privateStaticMethod(p1);
}
private static String privateStaticMethod(String p1){
return "dummy";
}
Я издевался над частным статическим методом, используя powermokito, следующим образом:
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "ClassUnderTest")
public class ClassUnderTestTest {
@Test
public void test_sometest() throws Exception {
PowerMockito.spy(ClassUnderTest.class);
PowerMockito.doReturn("whatever").when(ClassUnderTest.class, "privateStaticMethod","something");
String retValue = ClassUnderTest.methodUnderTest("something");
assertEquals(retValue, "whatever");
}
}
Теперь, есть ли способ проверить, что был вызван privateStaticMethod ?
Ответ №1:
Оказывается, это действительно сложно.
Если это можно заставить работать, решение, вероятно, соответствует тому, что написано здесь. К сожалению, у меня нет настройки PowerMockito, поэтому я не могу протестировать этот код самостоятельно:
// tell powermock(ito) that you want to do PARTIAL static mocking
PowerMockito.spy(ClassUnderTest.class);
// give it the mocking SPEC for that static method that needs mocking
PowerMockito.doReturn("whatever").when(ClassUnderTest.class, "privateStaticMethod","something");
// tell it to INVOKE the real method when the public one is called
PowerMockito.doCallRealMethod().when(Util, "methodUnderTest", any());
// now assert that the mock spec kicked in
assertThat(ClassUnderTest.methodUnderTest("something"), is("whatever"));
В приведенном выше примере используется is()
средство сопоставления Hamcrest, которое any()
было бы средством сопоставления аргументов (Power) Mockito. Убедитесь, что импорт выполнен правильно!
Вышеуказанное должно завершиться неудачей, когда вы измените свой общедоступный метод и не вызовете этот закрытый метод и / или вернете что-то еще.
Но реальный ответ таков: вам следует избегать такого рода тестов. Приведенный выше код неявно проверяет, был ли вызван этот частный метод: если он не вызван, вы должны получить другой результат!
Но реальный смысл здесь в том, что вы не должны тестировать такие вещи. Как ваш общедоступный метод получает свой результат, вообще не должно иметь значения для ваших тестов. Ваш общедоступный метод имеет контракт, и то, как это реализовано, является деталью реализации.
Ваш подход превращает ваш модульный тест в (чрезмерно сложную!) повторную реализацию вашего производственного кода. Это означает: как только вы намереваетесь изменить свою реализацию, ваш тест должен быть адаптирован. Хуже того, поскольку вы передаете имя метода в виде необработанной строки, вы даже не заметите этого до времени выполнения, когда внезапно метод перестанет возвращать whatever
, но выдаст вам dummy
.
Итак, реальный ответ здесь таков:
- прежде всего, избегайте статики, где это возможно, чтобы не заниматься издевательствами над статическими методами
- забудьте о тестировании таких деталей реализации.
Бессмысленно проверять, был ли вызван частный метод. Вызывающий ваш общедоступный метод должен возвращать информацию о том, «что входит и что возвращается». Все остальное не должно иметь значения на этом уровне.
И учитывая комментарий OP:
- для static гораздо больше проблем, чем просто «состояние». Это устраняет полиморфизм, а также приводит к прямой жесткой зависимости от класса, в котором находится статический код
- это усложняет не только модульное тестирование, но и любой вид теста
- и из моего личного опыта: когда люди говорят мне «Мне нужен PowerMock (ito) для моего недавно написанного кода», тогда я знаю, что они создали трудно тестируемый код без необходимости делать это. Абсолютно возможно (и желательно) писать производственный код способами, которые можно легко протестировать с менее навязчивыми фреймворками-макетами.
Другими словами: если вы не можете написать простой прямой тест в Mockito, то, скорее всего, вы создали сложный для тестирования производственный код, и вам было бы намного лучше потратить время и энергию, чтобы упростить тестирование производственного кода. Вместо того, чтобы использовать PowerMock (ito) hammer для «исправления» вашей проблемы, обходя симптомы. И да, это имеет значение. Мы исходили из основанной на PowerMock кодовой базы «слишком много статики» и в какой-то момент просто сказали людям «больше нет». С тех пор, когда нам нужен mocking, мы используем Mockito. И количество странных «сбоев модульного теста» из-за «изменения на статический» в другом месте уменьшилось до 0. И наш производственный код стал лучше.
Комментарии:
1. Я добавил PowerMockito. Выполните возврат («что угодно»). когда(ClassUnderTest.class , «anotherPrivateStaticMethod»,»что-то»); который даже не был вызван в тестовом пути, и тест пройден. Исходя из этого, я полагаю, что ничто не проверяется неявно. Кроме того, почему я не должен тестировать такие вещи. Это называется тестами взаимодействия, и такие взаимодействия необходимо тестировать. Powermock был создан только для тестирования статических взаимодействий. Когда состояние не сохраняется, статика не является проблемой
2. @RayS Прочитав еще немного, я переработал большую часть своего ответа. Я надеюсь, что А) техническое решение теперь работает для вас и Б) что мои доводы в пользу рекомендации ВООБЩЕ НЕ делать этого более убедительны. Помимо этого, я надеюсь, что вы цените время и энергию, которые я вложил сюда 😉