#java #юнит_тесты
Как можно это все проверить? Мои мысли: Внести фейковые данные в базу данных. Добавить данные через тест. Задать значения для проверки. Сравнить данные для проверки с данными из БД? Как это примерно реализовать? Просто с дата сервисами не работал особо вот и возникло затруднение. public class NewsDataService { private News news; private Listnewss; private DatabaseConnector db; private ResultSet result; public NewsDataService() throws ClassNotFoundException, SQLException { db = new DatabaseConnector(); newss = new ArrayList (); } public News getNews(int id) throws SQLException { result = db.query("SELECT * FROM news WHERE idnews = " + id); result = db.query("SELECT * FROM news WHERE idstudent = " + id); result = db.query("SELECT * FROM news WHERE idgroup = " + id); while (result.next()) { news.setDate(result.getString("date")); news.setTitle(result.getString("title")); news.setContent(result.getString("content")); } return news; } public News findByTitleNews(String title) throws SQLException { result = db.query("SELECT * FROM news WHERE title ='" + title + "'"); while (result.next()) { news.setTitle(result.getString("title")); news.setId(result.getInt("idnews")); news.setId(result.getInt("idgroup")); news.setId(result.getInt("idstudent")); news.setDate(result.getString("date")); news.setContent(result.getString("content")); } return news; } public List findAllNews() throws SQLException { result = db.query("SELECT * FROM news"); while (result.next()) { news.setTitle(result.getString("title")); news.setId(result.getInt("idnews")); news.setId(result.getInt("idgroup")); news.setId(result.getInt("idgroup")); news.setDate(result.getString("date")); news.setContent(result.getString("content")); } return newss; } public void save(News news) throws SQLException { String sql = "INSERT INTO news('title', 'content', 'date') " + "VALUES ('" + news.getTitle() + "', '" + news.getContent() + "', '" + news.getDate() + "',?);"; PreparedStatement pstmt = db.getConnect().prepareStatement(sql); pstmt.execute(); pstmt.close(); System.out.println(sql); System.out.println("News successful inserted"); } }
Ответы
Ответ 1
Протестировать текущий вариант класса NewsDataService - не самая тривиальная задача для решения, попробую объяснить почему. В процессе написания данного класса вы решили заинкапсулировать всю логику работы с БД внутри кода, дойдя до крайне низкого уровня - ручного составления SQL запросов. И теперь ваш класс служит крайне узкоспециализированной цели, поскольку он умеет только лишь создаваться и отвечать на запросы вида "дай мне сущность с id" и "сохрани такую-то сущность". Чтобы улучшить этот аспект кода (coupling), я бы порекомендовал бы вам почитать про Inversion of Control, Dependency Injection и про ORM. Я могу предложить 3 различных подхода к тестированию текущего варианта реализации NewsDataService, которые вполне как можно скомбинировать в тех или иных пропорциях Воспользуйтесь sandbox-based подходом - возьмите легковесную базу данных, заполните ее реальными значениями и напишите юнит-тесты без моков вообще. Вообще говоря, если бы вы писали такие тесты перед написанием кода (следуя методологии TDD), то вам бы сразу открылись многие минусы и неудобства использования написанного класса. Специально для таких целей можно использовать DbUnit, позволяющий упростить создание сендбоксовой базы данных и написание Setup / Teardown методов. Следуя принципам IoC, инициализируйте ваш DatabaseConnector в другом месте, а в NewsDataService прокидывайте его через конструктор. Это, во-первых, снимает с NewsDataService ответственность за инициализацию подключения, а во-вторых упрощает тестирование. Вот здесь уже сам по себе DatabaseConnector можно подменить mock-объектом, который будет эмулировать часть функциональности, например, на любой запрос SELECT будет возвращать предопределенную пачку новостей. Подход не самый лучший, потому что mock-объект должен быть предельно простым (иначе потребуются тесты на тесты), а этот факт сильно сковывает руки при тестировании. То есть, грубо говоря, вы не можете и не должны парсить запрос в вашем mock'e, поэтому максимум, что вы можете делать с его помощью - это разделять запросы SELECT / UPDATE, возвращая на них какую-нибудь заранее заданную чушь, ну и, например, проверять, что databaseConnector.query вообще был вызван. Общее правило достаточно простое - если код mock'a вызывает вопросы о том, правильно ли он работает для всех случаев, то это - плохой mock. Иногда, естественно, бывают ситуации, что без сложного mock'a не обойтись, но в таком случае нужно писать тесты на этот самый mock, что обычно бывает нежелательным. Выделить генерацию SQL запросов в отдельный класс типа SqlQueryProvider. В таком случае ваш текущий код начнет меняться примерно в следующую сторону: ... databaseConnector.query(sqlQueryProvider.getQueryToRetrieveById(...)); Такой подход уже позволяет отдельно покрыть тестами генерацию запросов, и, зная, что запросы к генерируются корректно, серьзно сэкономить на реальном тестировании NewsDataService. Опять же, для тестирования NewsDataService в таком случае может пригодиться mock на SqlQueryProvider. Несколько референсов по теме: Unit Testing Database Code Testing With DbUnit How do I unit test jdbc code in java? Всегда, кстати, хотелось посмотреть, как авторы советов типа "возьмите XYZMock, да заmock'айте эту обертку над БД и протестите, делов то" делают это на практике и сделали бы это, достанься им legacy код типа NewsDataService.Ответ 2
все зависит от поставленной задачи. если ваша задача проверить работу БД тогда для этого можно воспользоватся одним из уже существующих фреймворков. для субд оракл: ounit, plunit, pluto, utplsql (этот самый мощний) если вам нужно проверить работу программы ( хотя здесь вы должны уже быть уверены что СУБД работает правильно ). тогда вам следует придерживатся того плана который вы уже наметили. как реализовать - сначала создаете массив обьектов с тестовыми данными (можно опустить этот пункт но так проще незапутатся в данных). потом используете их для формирования запросов вставки данных в БД. и непосредственно во время теста, - выбираете все данные и проверяете только те обьекты которые вы создали во время теста, - сравниваете их с теми которые вы выбрали. они должны быть равны между собой (можно сравнивать по полям. можно для этого перегрузить метод equals внутри этих обьектов) но начать стоит с того чтоб исправить ошибки в коде: 1) private Listnewss - это должно быть локальной переменной в методе (во всех где используется). 2) private ResultSet result; - это должно быть локальной переменной в методе (во всех где используется) 3) PreparedStatement pstmt = db.getConnect().prepareStatement(sql); - вот эту переменную наоборот имеет смысл вынести за пределы метода и сделать полем. для того чтоб можно было использовать несколько раз. В самом же методе надос делать так: if(null == pstmt) { pstmt = db.getConnect().prepareStatement(sql); } вот вы в данном коде использовали PreparedStatement но если смотреть на остальной код то стает что вы не разобрались как это работает. Каждый раз когды вы отсылаете какой-то запрос СУБД она его сначала парсит. Эта операция очень важная и дорогостоющая, - потому что парсер у всей СУБД только один. Посему если слать много разных запросов паралельно то ваша СУБД будет работать очень медленно. Для того чтоб избежать однотипных запросов есть специальные средства для подстановки параметров. в JDBC это PreparedStatement. пример: String sql = "SELECT * FROM movies WHERE year_made = ?"; PreparedStatement ps = connection.prepareStatement(sql); ps.setInt(1,2002); ResultSet rs = ps.executeQuery(); // очистить установленные параметры можно так (необходимо для работы в цикле): ps.clearParameters(); Ответ 3
Чтобы проверить работу вашего класса в независимости от БД, стоит воспользоваться такой концепцией как Mock Objects, и заменить в тесте базу на мок. Почитайте об этом более подробно, статей достаточно. Из библиотек могу посоветовать EasyMock.
Комментариев нет:
Отправить комментарий