Как проверить локальные методы внутреннего класса в java? [дубликат]

этот вопрос уже есть ответ здесь:

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

до сих пор, когда я писал основной алгоритм, я создал частный метод для каждого под-алгоритма, как в примере ниже (OldStyle):

public class OldStyle {

    public int mainAlg() {
        int x = subAlg01();
        int y = subAlg02();
        int z = x * y;
        return z;
    }

    private int subAlg01() {
        return 3;
    }

    private int subAlg02() {
        return 5;
    }
}

это работало отлично, но мне не нравилось иметь распространение методов (subAlg01 и subAlg02), которые, даже если частные, использовались только одним методом (mainAlg).

недавно я диктовал использование локальных внутренних классов, и теперь мой пример (NewStyle):

public class NewStyle {

    public int mainAlg() {
        class Nested {

            public int subAlg01() {
                return 3;
            }

            public int subAlg02() {
                return 5;
            }
        }
        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();
        int z = x * y;
        return z;
    }
}

мне это очень нравится, но теперь у меня есть следующая проблема: как тестировать subAlg01 и subAlg02 с помощью JUnit?

кстати: я использую eclipse.

Спасибо за помощь.

редактировать: я пытаюсь объяснить лучше: у меня есть, скажем, алгоритм сортировки, и я хочу проверить его, чтобы убедиться, что он работает так, как ожидалось. Эти алгоритмы сортировки используются только методом m класса X. я мог бы сделать его частным методом класса X, но класс X обычно не имеет ничего общего с сортировкой, поэтому зачем "портить" класс X методом сортировки? Так Я поместил его внутрь метода м. Некоторое время спустя я хочу улучшить свой алгоритм сортировки (я делаю это быстрее), но я хочу быть уверенным, что это поведение, как ожидалось, поэтому я хочу повторно протестировать его с помощью оригинальных тестов.

это то, что я хочу сделать, может быть, нет решения, я надеюсь, что кто-то может мне помочь.

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

7 ответов


Филиппо, я понимаю твое разочарование проблемой и некоторыми ответами. Когда я впервые начал использовать JUnit много лет назад, я тоже хотел проверить личный код, и я подумал, что глупо со стороны гуру говорить, что это плохая идея.

оказывается, они были правы (сюрприз!) но только после написания нескольких тестов я понял почему. Возможно, вам придется пройти через тот же процесс, но в конечном итоге вы придете к тому же выводу ;-)

в любом случае, в вашей ситуации, я бы сделал Nested в надлежащий автономный класс, возможно, в отдельном пакете, чтобы сделать очевидным, что это вспомогательные классы. Затем я бы написал тесты для него напрямую, независимо от любых других тестов.

тогда я бы написал тесты для NewStyle и сосредоточьтесь только на поведении NewStyle.

(вполне вероятно, я бы впрыскивают Nested на NewStyle вместо создания экземпляра в NewStyle -- т. е. сделать его аргумент к NewStyle'конструктор С.

тогда, когда я пишу тесты для NewStyle в тесте я бы прошел в экземпляре Nested и продолжать. Если бы я чувствовал себя особенно сложно, я бы создал интерфейс из Nested и создать вторую реализацию, и проверить NewStyle С этим тоже.)


вы должны тестировать только открытый интерфейс классов, а не частные члены или частные внутренние классы. Частные члены должны быть деталями реализации, используемыми только общедоступными методами класса (прямо или косвенно). Таким образом, вы можете проверить их косвенно с помощью методов вызывающего абонента. Если вы чувствуете, что у вас недостаточно детализации в этих модульных тестах или что вы не можете почувствовать (некоторые) результаты, которые вас интересуют, это, вероятно, означает проблему с вашим дизайном: класс может быть слишком большой, пытаясь сделать слишком много, поэтому некоторые из его функций могут быть извлечены в отдельный класс, где он может быть непосредственно протестирован.

в текущем примере, если внутренний класс сам содержит много кода, Вы можете просто превратить его в класс верхнего уровня, тогда вы можете напрямую проверить его методы.

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


вы не можете достичь этих классов извне, поэтому junit не может их проверить. У вас должны быть публичные вещи, чтобы проверить их.


вы должны избегать усложнения кода только потому, что вы

не нравится распространение методы

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

public class NewStyle {

    public int mainAlg() {

        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();

        int z = x * y;
        return z;
    }

    class Nested {

        public int subAlg01() {
            return 3;

        }

        public int subAlg02() {
            return 5;
        }
    }
}

это может быть вызвано с помощью new NewStyle().new Nested().subAlg01();


Hm, я знаю, что такие языки, как Groovy, часто используются для модульного тестирования на Java и могут прозрачно обращаться к частным полям и методам.....но я не уверен в вложенных классах. Я могу понять, почему вы можете захотеть сделать это, но я занимаю ту же позицию, что и с тестированием геттеров и сеттеров, они должны быть протестированы как побочный эффект тестирования ваших публичных методов, и если они не тестируются таким образом, то почему они там для начала?


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

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

но, как было сказано ранее, если ваш внутренний класс никогда не используется внешней стороной, то это просто деталь реализации, и тестирование его отдельно просто не полезно.


на самом деле, вы можете проверить локальный внутренний класс. Но он должен реализовать интерфейс. Таким образом, вы можете создать экземпляр локального класса через отражение и привести его к его интерфейсу. После того, как у вас есть тип интерфейса, вы можете проверить свой код реализации без проблем:

интерфейс:

public interface Algorithm {
    int subAlg01();
    int subAlg02();
}

класс:

public class NewStyle {
    public int mainAlg() {
        class Nested implements Algorithm {

            public int subAlg01() {
                return 3;
            }

            public int subAlg02() {
                return 5;
            }
        }
        Nested n = new Nested();
        int x = n.subAlg01();
        int y = n.subAlg02();
        int z = x * y;
        return z;
    }
}

тест:

public class NewStyleTest {

    @Test
    public void testLocal() throws ClassNotFoundException,
            NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        Class<?> forName = Class.forName("NewStyleNested");
        Constructor<?> declaredConstructor = forName
                .getDeclaredConstructor(NewStyle.class);
        declaredConstructor.setAccessible(true);
        Algorithm algorithm = (Algorithm) declaredConstructor
                .newInstance(new NewStyle());

        assertEquals(algorithm.subAlg01(), 3);
        assertEquals(algorithm.subAlg02(), 5);
    }
}