eConti - программирование в вопросах и ответах

Тестовый пример Junit для частного метода

У меня класс ниже. Я новичок в написании тестов junit. Мне нужно написать тестовый пример для этого. Как написать тестовый метод для метода startSchemaMaintenance в тестовом классе, так как он вызывает закрытый метод без аргументов?

public class SchemaMaintenance {

    //Loading statuses overview
    // NOT_STARTED = 0
    // START_LOADING = 1
    // IN_PROGRESS = 2
    // COMPLETED = 3
    // LOADING_ERROR = 4
    private static volatile Integer loading_status = 0;

    public void startSchemaMaintenance() throws Exception {
        if (checkLoadingStatus() == 1) {
            doSchemaMaintenance();

            loading_status = 3;
        }
    }

    private void doSchemaMaintenance(){
        //Do something....
    }

    private int checkLoadingStatus() throws Exception {
        if (loading_status==0 ||loading_status == 2) {
            synchronized (loading_status) {
                if (loading_status==0) {
                    loading_status = 2;
                    return 1;
                }else if(loading_status == 2) {
                    while(loading_status == 2);

                    if((loading_status == 4)){

                        throw new Exception("status = " + 4);
                    }
                }else if(loading_status == 4) {

                    //log.error(generateErrorMessage());
                    throw new Exception("status = " + 4);
                }
            }
        }else if((loading_status == 4)){
            //log.error(generateErrorMessage());
            throw new Exception("status = " + 4 );
        }

        return loading_status;

    }
}
13.02.2020

  • проверьте его, проверив метод, который его вызывает. 13.02.2020
  • вы имеете в виду метод, вызывающий startSchemaMaintenance 13.02.2020
  • нет. Вы понимаете, что то, что вы опубликовали, не совсем подходит для Java, верно? Вы устанавливаете значение loadStatus в своем тесте и вызываете свой метод 13.02.2020
  • не валидная java?? 13.02.2020
  • Обслуживание схемы класса недопустимо для Java. Каково настоящее имя вашего файла? 13.02.2020
  • Лучший способ протестировать закрытый метод — не тестировать его напрямую. в противном случае вы можете предоставить доступ к этому методу на уровне пакета, указав модификатор по умолчанию и написав тестовый класс в том же пакете в тестовой папке. 13.02.2020

Ответы:


1

Прежде всего пара вещей:

  1. Почему loading_status статичен? В основном 2 экземпляра этого класса будут использовать одну и ту же переменную loading_status. loading_status либо не должен быть статическим, либо checkLoadingStatus также должен быть статическим.
  2. loading_status ужасно назван. Если вы хотите, чтобы он оставался статичным, вы должны назвать его LOADING_STATUS. Если он не будет статичным, назовем его loadingStatus. Это соглашение Java.
  3. Вы должны создать правильный тип перечисления для loading_status, а не целое число.
  4. while (loading_status == IN_PROGRESS); это просто плохая практика. По крайней мере, что вы можете сделать, это: while(loading_status == IN_PROGRESS) { Thread.sleep(100); }; Однако тайм-аут является лучшей практикой. Что-то вроде:
long timeoutMillis = 5000L; // Should come from a configuration or something
long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (loadingStatus == IN_PROGRESS) {
    long remainingMillis = end - System.currentTimeMillis();
    if (remaining <= 0) {
        // Timeout while loading
        loadingStatus = LOADING_ERROR;
        break;
    }
    Thread.sleep(50);
}
// ...

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

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

src
+-- main
+-- +-- com/app/MyClass.java
+-- test
    +-- com/app/MyClassTest.java // this test class will able to access package-private methods in MyClass
13.02.2020
  • Как я могу изменить переменную состояния загрузки при тестировании метода startSchemaMaintenance?? 13.02.2020
  • Вы не знаете. Вы должны немного реструктурировать свой код, чтобы сделать его более тестируемым. Я не большой сторонник разработки через тестирование, но в вашем случае это может помочь структурировать ваш код таким образом, чтобы вы думали о том, какие тестовые случаи у вас будут. Я написал это в Notepad++ и не проверял, но вы понимаете, что я имею в виду: pastebin.com/uiSydTXU 13.02.2020

  • 2

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

    Есть несколько вещей, которые вы можете сделать, чтобы сделать его пригодным для тестирования:

    1. Перепишите свой код, чтобы разделить метод на более мелкие. Всегда нужно делать там, где это уместно.

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

    3. Вы можете сделать метод защищенным, чтобы вы могли наследовать свой класс в своем тесте и косвенно вызывать этот метод над вашим унаследованным классом.

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

    Если вы склонны изменять видимость метода для пакета private или protected, вы можете аннотировать свой метод как @VisibleForTesting, чтобы такие инструменты, как Sonar и другие члены команды, знали, почему он не является закрытым.

    Надеюсь, поможет.

    13.02.2020

    3

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

    package com.test
    
    import com.test.SchemaMaintenance;
    import org.junit.Test;
    import org.mockito.internal.util.reflection.Whitebox;
    
    public class SchemaMaintenanceTest {
    
        @Test
        public void TestSchema() throws Exception {
            SchemaMaintenance schema = new SchemaMaintenance();
            Whitebox.setInternalState(schema,"loading_status",2);
            schema.startSchemaMaintenance();
        }
    
    }
    

    Предположим, вы должны включить в свой проект зависимость mockito:

    напр. Мейвен

    <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>1.6.4</version>
    </dependency>
    
    13.02.2020
  • Он говорит, что имя поля экземпляра loading_status не может быть найдено в иерархии классов SchemaMaintenanceTest, когда я добавляю verify(dataBaseSchemaMaintenance, times(0)).doSchemaMaintenance(); 13.02.2020
  • Можете ли вы поделиться всем кодом модульного теста и выводом исключения stacktrace? 13.02.2020
  • Кроме того, когда я отлаживаю класс SchemaMaintenance, он также не показывает значение loading_status как 2, оно по-прежнему равно 0. 13.02.2020
  • Вы используете точный код, который я предоставил в своем ответе? Я протестировал его, и он работает. В частности, когда я отлаживаю код, которым я поделился, для переменной loading_status устанавливается значение 2. 13.02.2020

  • 4

    Вот подход, который сработал для меня.

    1. Make a public "tester" inner class in your class.
      • If you are testing an inner class the "tester" still needs to be on the outer class.
    2. Тестер — это делегат для частных методов, которые вы хотите протестировать.
    3. Добавьте в свой класс метод для получения экземпляра тестера.
    4. В вашем тестовом методе получите экземпляр тестера и
    5. Вызовите свой частный метод через соответствующий метод экземпляра тестера.

    Будут разные варианты в зависимости от требуемого доступа к методам и внутренним классам и т.д.

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

    //  Outer owner class
    //
    public class OwnerClass Class {
        
        private class InnerClass {
                : 
                
            //  method I'm testing
            //
            private void doSomething)( String name ){
                ...
            }
        }
        
            :
        
        //  Tester class
        //
        public class InnerClassTester {
            
            private final InnerClass        innerClass    = new InnerClass();
                :
                
            //  test invocatiion
            //
            private void doSomething)( String name ){
                
                innerClass.doSomething)( name )
            }
        }
    
        //  Get an instance of the tester
        //    
        public InnerClassTester getInnerClassTester() {
            return new InnerClassTester();
        }
    

    Внутри вашего модульного теста...

    @Test
    public void doSomethingTest() throws Exception {
        
        OwnerClass ownerClass = new OwnerClass();
        innerClassTester      = ownerClass.InnerClassTester();
                :
                
        //  Test the private method
        //
        innerClassTester.doSomething( "123456" );
    }
    

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

    23.07.2021
    Новые материалы

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

    Время расцвета закончилось
    Большую часть своей карьеры в индустрии программного обеспечения программисты работали с головой в песок. Успех в отрасли требует навыков презентации и обучения других. Ценность улучшенных..

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

    Состояние совместной фильтрации в 2022 году, часть 1
    ResBeMF: Улучшение прогнозируемого охвата совместной фильтрации на основе классификации (arXiv) Автор: Анхель Гонсалес-Прието , Авраам Гутьеррес , Фернандо Ортега , Рауль Лара-Кабрера..

    Зачем изучать PYTHON в 2022 году !
    Python — востребованный, доступный язык программирования с активным, постоянно растущим сообществом пользователей. Для тех, кто хочет сменить профессию в мире технологий с помощью..

    Решение капч с помощью Puppeteer
    Это руководство предназначено для текстовых кодов, а не для reCAPTCHA Google (см. конец этого сообщения). Требования: Антикапча или любой другой сервис по разгадыванию капчи. Модуль..

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