Страницы

Поиск по вопросам

вторник, 8 января 2019 г.

JUnit тесты для DataService

Как можно это все проверить? Мои мысли: Внести фейковые данные в базу данных. Добавить данные через тест. Задать значения для проверки. Сравнить данные для проверки с данными из БД? Как это примерно реализовать? Просто с дата сервисами не работал особо вот и возникло затруднение. public class NewsDataService { private News news; private List newss; 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"); } }


Ответ

Протестировать текущий вариант класса 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.

Комментариев нет:

Отправить комментарий