вопрос по mock-объектам(для баз данных)

Модератор: Absurd

Ответить
Аватара пользователя
AiK
Сообщения: 2287
Зарегистрирован: 13 фев 2004, 18:14
Откуда: СПб
Контактная информация:

Забавная мешанина. URL - строка коннекции для SQL сервера, где eplsp001 его имя.
С драйвером всё вроде бы понятно.
Mock-объект понятие мне не знакомое, но навевает какие-то смутные воспоминания о JUnit.
А вот строка JNDI Name ставит меня в тупик.

Итого: есть предложение переехать в раздел Java, там у нас минимум два спеца обитают, которые собаку съели на Java :)
Даже самый дурацкий замысел можно воплотить мастерски
Аватара пользователя
AiK
Сообщения: 2287
Зарегистрирован: 13 фев 2004, 18:14
Откуда: СПб
Контактная информация:

Всё-таки мы переехали в раздел Java.

Теперь по теме. На сколько я понимаю концепцию JDBC и JUnit, с одной стороны твой java код не должен меняться при переезде от одной СУБД к другой (при условии конечно что сами БД идентичны), а с другой стороны JUnit позволяет тестировать именно реальные коды (классы), а не какие-то невразумительные конструкции.

Итак, начнём танцевать от твоего кода, пока без тестов.

Где-то в твоём классе должен существовать метод а-ля

Код: Выделить всё

public void connect (String URI) throws SQLException{
    //XXX: подгрузку драйвера опустил
   connection = DriverManager.getConnection(URI);
}
для получения коннекции.

Для красоты пусть у нас ещё будет метод

Код: Выделить всё

public void exec(String Query) SQLException{

    Statement statement = connection.createStatement();
    ResultSet result =
    statement.executeQuery(Query);
    //XXX: дальше стандартное выфетчивание резалтсета
}
В тесте же, ты создаёшь свой крутой объект и вызываешь последовательно его методы connect() и exec().

Как же отвязаться от базы? А очень просто. Для начала разберёмся с резалтсетами.
Здесь до посинения просто создаём нужные нам резалтсеты при помощи StatementResultSetHandler.createResultSet();
и заполняем их при помощи addRow (ну или из файла подгружаем, кому как больше нравится :) ) Тем самым полностью отвязываемся от БД и проверяем поведение нашего exec() на различных наборах "возвращаемых" данных.
Ну а с коннекцией вообще всё тривиально. Она вся из себя одинаковая для всех источников. Т.е. метод getMockConnection() не имеет параметров.
Остался последний вопрос - как подпихнуть созданные нами резалтсеты классу, который мы тестируем? А достаточно ему подсунуть нашу коннекцию - как видно выше, резалтсет-то мы получаем именно у неё. И тут как раз в моём понимании кроются все тонкие моменты. Если мы реализуем наш класс таким образом, чтобы его методы получали коннекцию в качестве одного из параметров, то проблем нет - запроси у фреймворка коннекцию и отдай.
Если я запрашиваю коннекцию у DriverManager'а, то насколько я понимаю, фреймворк сам вернёт коннекцию, не зная при этом ничего ни о базе, ни о драйверах.

Вот только как он это делает, я не понимаю. Если быть более точным, я не имею представления, как работает DriverManager. Поэтому у меня есть некоторые сомнения о поведении теста, если до вызова мэнеджера будет прогружен нужный драйвер явным образом, т.е. через Class.forName(...)

Сорри за сумбур - разбирался в этой кухне прямо по ходу пьесы :)

Итого: что за зверь setURI мне абсолютно непонятно, а один из возможных ответов на исходный вопрос такой:

Код: Выделить всё

    Connecttion connection;
    ...

    //setup
    protected void setUp() throws Exception{
        super.setUp();
        connection = getJDBCMockObjectFactory().getMockConnection();
    }
    //constructor
    public void testSQL() throws Exception{
        //XXX: пропущен для ясности :)
        prepareResultSet();
        MyCoolClass obj = new MyCoolClass();
        obj.connect(...);
        obj.execute(connection, new String("select * from cooltable"));
        verifyAllResultSetsClosed();
        verifyConnectionClosed();
        ...
    }
Даже самый дурацкий замысел можно воплотить мастерски
Аватара пользователя
AiK
Сообщения: 2287
Зарегистрирован: 13 фев 2004, 18:14
Откуда: СПб
Контактная информация:

Ага. Теперь всё встало на свои места - JDBCMockObjectFactory дерегистрирует все драйвера DriveManager'а и регистрирует MockDriver. Итого, DriverManager.getConnection возвращает именно то что нужно, т.е. MockConnection.
И, попутно, у меня родилось сомнение, что вопрос изначально ставился о том, как реализовать собственный
mockrunner, а я тут распинаюсь как его использовать :)
Даже самый дурацкий замысел можно воплотить мастерски
Наталья
Сообщения: 3
Зарегистрирован: 29 июл 2004, 12:45

Большое спасибо, AiK, за столь обстоятельный ответ :) . Ты мне очень помог - я смогла решить свои проблемы. На случай, если кому - то интересно, расскажу, какая фактически была проблема, и как ее можно решить.
Я тестила ejb-класс, который обращается к базе данных. Первым этапом, как любезно подсказал AiK, является создание заместителя нашей базы следующим кодом:

Код: Выделить всё


        /* We need to set MockContextFactory as our JNDI provider.
         * This method sets the necessary system properties.
         */
        MockContextFactory.setAsInitial();
        // create the initial context that will be used for binding EJBs
        context = new InitialContext( );

        JDBCMockObjectFactory factory = createJDBCMockObjectFactory();
        MockDataSource mds = factory.getMockDataSource();
        con = (MockConnection) mds.getConnection();
        context.rebind("jndiname", mds); 
        MockResultSet mockSet = new MockResultSet("set");
        ArrayList names = new ArrayList();
        mockSet.addColumn("name");
        names.add(0,"blue");
        mockSet.addRow(names);
        names.add(0,"pink");
        mockSet.addRow(names);
        con.getStatementResultSetHandler().prepareResultSet("select name from R_Solution where SOL_ID = 20",mockSet);
context. rebind позволит теперь в любой функции, которую вызывает тестируемый код, определять и обращаться к нашей "подмене"

Код: Выделить всё

            ic = new InitialContext();
            DataSource ds = (DataSource)ic.lookup("jndiname");
            conn = ds.getConnection();
//...
вот так :-)
Ответить