JMockit NullPointerException в блоке исключений?

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

Используя JMockit для тестирования некоторых из моих собственных классов JDBC «Wrapper», я зашел в тупик.

Это класс, который я тестирую:

public class JdbcConnectionProperties {
    private Properties properties = new Properties();
    private String username;
    private String password;
    private String connectionString;

    public JdbcConnectionProperties(String propertiesFilePath) {
        loadProperties(propertiesFilePath);
    }

    public void setProperties() {
        username = properties.getProperty("user");
        password = properties.getProperty("password");

        String connectionType = properties.getProperty("connection_type");
        String serverAddress = properties.getProperty("server_address");
        String port = properties.getProperty("port");
        String sid = properties.getProperty("sid");

        //Create a connection string
        connectionString = "jdbc:oracle:" + connectionType + ":@" + serverAddress + ":" + port + ":" + sid;
    }


    private void loadProperties(String propertiesFilePath) {
        String filePath = Thread.currentThread().getContextClassLoader().getResource(propertiesFilePath).getFile();
        //Load properties from classpath
        try {
            properties.load(new FileInputStream(filePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getConnectionString() {
        return connectionString;
    }

    public Properties getProperties() {
        return properties;
    }
}

Это тест:

public class JdbcConnectionPropertiesTest {

    @Test
    public void testSetProperties(
//            @Mocked final Properties properties
    ) throws Exception {

        //Mock loadFilePath method so i dont end up mocking a ton of classes
        new MockUp<JdbcConnectionProperties>() {
            @Mock
            void loadProperties(String propertiesFilePath) {
                //Doing nothing, simple "stub" method
            }
        };

        JdbcConnectionProperties jdbcConnectionProperties = new JdbcConnectionProperties("bla");
//        Deencapsulation.setField(jdbcConnectionProperties, "properties", properties);
//        Mockit.stubOutClass(JdbcConnectionProperties.class, "loadProperties");
        final String username = "username";
        final String password = "password";
        final String connectionType = "thin";
        final String serverAddress = "localhost";
        final String port = "1521";
        final String sid = "orcl";
        String connectionString = "jdbc:oracle:" + connectionType + ":@" + serverAddress + ":" + port + ":" + sid;

        new Expectations() {
            @Mocked
            Properties properties;

            {
                properties.get("user");
                result = username;

                properties.get("password");
                result = password;

                properties.get("connection_type");
                result = connectionType;

                properties.get("server_address");
                result = serverAddress;

                properties.get("port");
                result = port;

                properties.get("sid");
                result = sid;
            }
        };

        jdbcConnectionProperties.setProperties();

        Assert.assertEquals("Incorrect user", username, jdbcConnectionProperties.getUsername());
        Assert.assertEquals("Incorrect password", password, jdbcConnectionProperties.getPassword());
        Assert.assertEquals("Incorrect connection string", connectionString, jdbcConnectionProperties.getConnectionString());
    }
}

Пара замечаний. Я попытался вставить издевательские свойства в объект с помощью Deencapsulation (я оставил их прокомментированными в коде).

Я попытался просто издеваться над ним с помощью аннотации @Mocked.

Я попытался заглушить его с помощью stubOutClass.

Это не первый тест, который я пишу, но я относительно новичок в JMockit. Тесты, которые я написал раньше, никогда не вызывали у меня головной боли, как этот. Я думаю, что написал около 20-30 тестов с JMockit, и у меня никогда не было таких проблем.

Ошибка (во всех упомянутых сценариях):

java.lang.NullPointerException
    at java.util.Hashtable.get(Hashtable.java:335)
    at jdbc.JdbcConnectionPropertiesTest$2.<init>(JdbcConnectionPropertiesTest.java:49)
    at jdbc.JdbcConnectionPropertiesTest.testSetProperties(JdbcConnectionPropertiesTest.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:71)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:199)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Класс очень простой. Тест должен быть очень простым. Но почему-то тест вылетает на блоке Expectations (ожидание первого свойства). Если я прокомментирую первые, то он продолжает бросать его на следующий. Пробовал любую, любую строку для сопоставления аргументов.

Как я это вижу, я издеваюсь над JdbcConnectionProperties loadProperties, чтобы упростить тестирование. Затем я передаю в тест издевательский объект свойств.

А потом...

...он должен работать. Кстати, я никогда не видел исключения такого масштаба в блоке исключений.

Спасибо.


person pfh    schedule 27.09.2011    source источник


Ответы (2)


Hashtable#get — это один из немногих методов, которые JMockit не имитирует по умолчанию, потому что он может мешать JDK или самому JMockit при имитации. Вы можете заставить этот конкретный тест работать, явно попросив его смоделировать с помощью @Mocked("get").

Однако может быть проще просто использовать в тесте реальный файл «.properties», без насмешек.

person Rogério    schedule 27.09.2011
comment
Можете ли вы объяснить, почему? Потому что он синхронизирован (потокобезопасность)? Есть ли список методов, которые могут вызвать такие проблемы? Решение, которое я предоставил, простое и правильное, но если у вас есть ресурс, обсуждающий упомянутое поведение, это поможет в будущем. Спасибо. - person pfh; 28.09.2011
comment
Список есть, но он в коде (см. класс ExpectationsModifier). Причина в том, что когда JMockit имитирует класс, он временно переопределяет его байт-код в JVM, затрагивая все экземпляры и вызовы всех методов, независимо от того, откуда они поступают (на практике существует фильтрация вызовов, которые происходят во время загрузки класса). В будущем такого рода проблемы будут устранены путем модификации байт-кода в тестируемом (вызывающем) коде, а не в фиктивном коде; но мне потребуется некоторое время, чтобы реализовать это. - person Rogério; 30.09.2011

new Expectations() {
            @Mocked("getProperty")
            Properties properties;

            {
                properties.getProperty("user");
                result = username;

                properties.getProperty("password");
                result = password;

                properties.getProperty("connection_type");
                result = connectionType;

                properties.getProperty("server_address");
                result = serverAddress;

                properties.getProperty("port");
                result = port;

                properties.getProperty("sid");
                result = sid;
            }
        };

Спасибо Рожерио. Как он указал, причина в «внутреннем» насмешливом классе. Очень небольшое ограничение, которое следует иметь в виду.

Другие классы, которые требуют некоторого внимания (надеюсь, я смогу написать это):

Классы, о которых следует помнить при использовании JMockit

person pfh    schedule 28.09.2011