#java #unit-testing
#java #модульное тестирование
Вопрос:
Хорошая ли это идея — изменить private class members на default (доступ к пакету) для тестирования их поведения? Я имею в виду, что тестовый пример должен быть размещен в тестовом каталоге, но в том же пакете, что и класс тестируемого элемента.
РЕДАКТИРОВАТЬ: Все, что вы, ребята, говорите правду. Но классы часто имеют вспомогательные частные методы. И эти методы могут быть сложными, поэтому их необходимо протестировать. И это очень плохо — тестировать общедоступные методы для обеспечения корректной работы частных сложных методов. Вы так не думаете?
Ответ №1:
Обычно я предпочитаю писать свои классы и тесты таким образом, чтобы написание тестов с использованием общедоступного API имело смысл. Итак, по сути, я хочу сказать, что если вам нужно получить доступ к закрытому состоянию вашего тестируемого класса, вы, вероятно, уже слишком вовлечены во внутренние компоненты этого класса своим тестом..
Комментарии:
1. И если вы хотите разрешить классу проверять себя, вы можете использовать что-то вроде JML для сжатия класса.
2. 1 И, тестируя общедоступный API, вы в любом случае неявно тестируете private API.
3. 1 Если вы не можете протестировать закрытый метод через общедоступный API, удалите его, поскольку к нему невозможно получить доступ. 😉
Ответ №2:
Нет, это не так. Потому что изменение тестового объекта может изменить результат. Если вам действительно нужно вызвать закрытые элементы или методы во время тестирования, безопаснее добавить средство доступа. Это все равно изменяет класс, но с меньшим риском. Пример:
private void method() { /* ... */ }
// For testing purpose only, remove for production
@Deprecated // just another way to create awareness ;)
void testMethod() {
method();
}
ХОРОШО — еще одно решение, если вам нужно протестировать закрытые методы: вы можете вызвать любой метод с помощью reflection и instantiation API.
Предполагая, что мы имеем:
public class SomeClass {
private Object helper(String s, String t) { /* ... / }
}
затем мы можем протестировать его следующим образом
@Test public void testHelper() {
try {
SomeClass some = new SomeClass();
Method helperMethod = some.getClass().getDeclaredMethod("helper", String.class, String,class);
helperMethod.setAccessible(true);
Object result = helperMethod.invoke(some, "s", "t");
// do some assert...
catch(Exception e) {
// TODO - proper exception handling
}
}
Комментарии:
1. Спасибо, это хорошая идея, но плохая практика. Количество ненужных методов будет расти, и это плохо.
2. @Michael Z — Я знаю. Такая же плохая практика, как тестирование частных методов 😉 Если вы не возражаете против изменения видимости — тогда сделайте это . В противном случае: используйте существующий или предоставьте новый visible API для тестирования ваших вспомогательных методов.
Ответ №3:
Я понимаю, что вы имеете в виду, говоря о необходимости тестирования частных методов, и я также понимаю, почему люди говорят, что тестируют только общедоступные методы. Я только что столкнулся с некоторым устаревшим кодом, который содержит множество закрытых методов, некоторые из которых вызываются общедоступными методами, но некоторые являются потоками или вызываются потоками, которые запускаются при создании объекта. Поскольку код изобилует ошибками и в нем отсутствуют какие-либо комментарии, я вынужден протестировать закрытый код. Я использовал этот метод для решения проблемы.
MainObject.cs
class MainObject
{
protected int MethodOne(); // Should have been private.
....
}
TestMainObject.cs
class ExposeMainObject : MainObject
{
public int MethodOne();
}
class TestMainObject
{
public void TestOne()
{
}
}
Поскольку тестовые объекты не поставляются, я не вижу в этом проблемы, но если есть, пожалуйста, сообщите мне.
Ответ №4:
Тестирование превосходит модификаторы конфиденциальности. Действительно, как часто ошибка возникает из-за «слишком большой» видимости метода? По сравнению с ошибками, вызванными методом, который не был полностью протестирован?
Было бы неплохо, если бы в Java была опция «друг», как в C . Но ограничение в языке никогда не должно быть оправданием для отказа от тестирования чего-либо.
Майкл Фезерс участвует в этом обсуждении в книге «Эффективная работа с устаревшим кодом» (отличная книга) и предполагает, что это может быть признаком подкласса, который хочет быть извлечен (и иметь общедоступные методы).
В нашем магазине (~ 1 МЛН LOC) мы заменяем ‘private’ на ‘/TestScope/’ в качестве индикатора того, что метод должен быть фактически закрытым, но все еще тестируемым.
Попытка обойти ‘private’ с помощью reflection — это, ИМХО, неприятный запах. Это усложняет написание, чтение и отладку тестов, чтобы сохранить «фетиш» конфиденциальности, с которым вы все равно работаете. Зачем беспокоиться?