Как проверить локальные методы внутреннего класса в 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 с помощью метода сортировки? Поэтому я помещаю его в метод m. Некоторое время спустя я хочу улучшить алгоритм сортировки (я делаю это быстрее), но я хочу быть уверенным, что это поведение, как и ожидалось, поэтому я хочу перепроверить его с помощью оригинальных тестов.

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

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

Ответы

Ответ 1

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

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

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

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

(Вероятно, я бы также ввел Nested в NewStyle вместо того, чтобы создавать его в NewStyle - то есть сделать его аргументом для конструктора NewStyle.

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

В вашем случае вы можете протестировать методы, если они являются методами внешнего класса, или если вам абсолютно необходим внутренний класс, то поместите его вне метода 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();

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

Интерфейс:

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("NewStyle$1Nested");
        Constructor<?> declaredConstructor = forName
                .getDeclaredConstructor(NewStyle.class);
        declaredConstructor.setAccessible(true);
        Algorithm algorithm = (Algorithm) declaredConstructor
                .newInstance(new NewStyle());

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