Java DAO de mise en œuvre de tests
C'est très simple DAO tentative que j'aimerais partager.
Ma question est si c'est une bonne façon de tester un DAO. Ce que je veux dire c'est que je suis la vérification de la requête SQL et en lui donnant de retour d'une fantaisie. Alors dites-le simulacre de retour de ces valeurs et de les opposer?
J'ai mis à jour le DAO utiliser instruction préparée au lieu d'une simple Déclaration. Merci.
public class PanelDao implements IO {
private final static Logger LOGGER = Logger.getLogger(PanelDao.class);
private Connection connection;
public PanelDao() throws SQLException {
this(MonetConnector.getConnection());
}
public PanelDao(Connection connection) throws SQLException {
this.connection = connection;
}
@Override
public void save(Panel panel) throws SQLException {
final String query = "INSERT INTO panels VALUES ( ?, ?, ?, ?, ?, ?, ? )";
final PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, panel.getId());
statement.setString(2, panel.getColor());
statement.setDate(3, (new Date(panel.getPurchased().getTime())) );
statement.setDouble(4, panel.getCost());
statement.setDouble(5, panel.getSellingPrice());
statement.setBoolean(6, panel.isOnSale());
statement.setInt(7, panel.getUserId());
LOGGER.info("Executing: "+query);
statement.executeUpdate();
}
@Override
public void update(Panel object) {
throw new UnsupportedOperationException();
}
@Override
public void delete(Panel object) {
throw new UnsupportedOperationException();
}
@Override
public Panel find(String id) throws SQLException {
final String query = "SELECT * FROM panels WHERE id = ? ";
final PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, id);
LOGGER.info("Executing: "+query);
final ResultSet result = statement.executeQuery();
final Panel panel = new Panel();
if (result.next()) {
panel.setId(result.getString("id"));
panel.setColor(result.getString("color"));
}
return panel;
}
}
Et la classe de test
public class PanelDaoTest {
@InjectMocks
private PanelDao panelDao;
@Mock
private Connection connection;
@Mock
private Statement statement;
@Mock
private ResultSet result;
private Panel panel;
@BeforeClass
public static void beforeClass() {
BasicConfigurator.configure();
}
@Before
public void init() throws SQLException {
MockitoAnnotations.initMocks(this);
Mockito.when(connection.createStatement()).thenReturn(statement);
panel = new Panel("AZ489", "Yellow", new Date(), 10.00, 7.50, true, 1);
}
@Test
public void testSave() throws SQLException {
Mockito.when(connection.prepareStatement("INSERT INTO panels VALUES ( ?, ?, ?, ?, ?, ?, ? )")).thenReturn(statement);
panelDao.save(panel);
Mockito.verify(statement).executeUpdate();
}
@Test
public void testFind() throws SQLException {
Mockito.when(connection.prepareStatement("SELECT * FROM panels WHERE id = ? ")).thenReturn(statement);
Mockito.when(statement.executeQuery()).thenReturn(result);
Mockito.when(result.next()).thenReturn(true);
Mockito.when(result.getString("id")).thenReturn("AZ489");
Mockito.when(result.getString("color")).thenReturn("Yellow");
Panel panel = panelDao.find("AZ489");
assertEquals("AZ489",panel.getId());
assertEquals("Yellow",panel.getColor());
Mockito.verify(statement).executeQuery();
}
}
2.0 Tests de DAO avec HSQLDB
Après prise en compte de vos commentaires j'ai décidé d'utiliser HSQLDB pour de vrai tests de bases de données. Vous trouverez comme d'une ressource si confrontés à des problèmes similaires.
public class PanelDao implements IO {
private final static Logger LOGGER = Logger.getLogger(PanelDao.class);
private Connection connection;
/**
* Default constructor is using Monet connector
*/
public PanelDao() throws SQLException {
this(MonetConnector.getConnection());
}
public PanelDao(Connection connection) throws SQLException {
this.connection = connection;
}
@Override
public void save(Panel panel) throws SQLException {
final String query = "INSERT INTO panels VALUES ( ?, ?, ?, ?, ?, ?, ? )";
final PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, panel.getId());
statement.setString(2, panel.getColor());
statement.setDate(3, (new Date(panel.getPurchased().getTime())) );
statement.setDouble(4, panel.getCost());
statement.setDouble(5, panel.getSellingPrice());
statement.setBoolean(6, panel.isOnSale());
statement.setInt(7, panel.getUserId());
LOGGER.info("Executing: "+query);
statement.executeUpdate();
}
@Override
public void update(Panel object) {
throw new UnsupportedOperationException();
}
@Override
public void delete(Panel object) {
throw new UnsupportedOperationException();
}
@Override
public Panel find(String id) throws SQLException {
final String query = "SELECT * FROM panels WHERE id = ? ";
final PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, id);
LOGGER.info("Executing: "+query);
final ResultSet result = statement.executeQuery();
if (result.next()) {
final Panel panel = new Panel();
panel.setId(result.getString("id"));
panel.setColor(result.getString("color"));
panel.setPurchased(new Date(result.getDate("purchased").getTime()));
panel.setCost(result.getDouble("cost"));
panel.setSellingPrice(result.getDouble("selling_price"));
panel.setOnSale(result.getBoolean("on_sale"));
panel.setUserId(result.getInt("user_id"));
return panel;
}
return null;
}
}
et la classe de Test:
public class PanelDaoTest {
private PanelDao panelDao;
private Panel panel;
/* HSQLDB */
private static Server server;
private static Statement statement;
private static Connection connection;
@BeforeClass
public static void beforeClass() throws SQLException {
BasicConfigurator.configure();
server = new Server();
server.setAddress("127.0.0.1");
server.setDatabaseName(0, "bbtest");
server.setDatabasePath(0, ".");
server.setPort(9000);
server.start();
PanelDaoTest.connection = DriverManager.getConnection("jdbc:hsqldb:hsql://127.0.0.1:9000/bbtest", "SA", "");
PanelDaoTest.statement = PanelDaoTest.connection.createStatement();
}
@Before
public void createDatabase() throws SQLException {
PanelDaoTest.statement.execute(SqlQueries.CREATE_PANEL_TABLE);
panelDao = new PanelDao(PanelDaoTest.connection);
}
@Test
public void testSave() throws SQLException {
panel = new Panel();
panel.setId("A1");
panel.setPurchased(new Date());
panelDao.save(panel);
assertNotNull(panelDao.find("A1"));
}
@Test
public void testFind() throws SQLException {
final String id = "45ZZE6";
panel = Panel.getPanel(id);
Panel received = panelDao.find(id);
assertNull(received);
panelDao.save(panel);
received = panelDao.find(id);
assertNotNull(received);
assertEquals(panel.getId(), received.getId());
assertEquals(panel.getColor(), received.getColor());
assertEquals(panel.getPurchased().getDate(), received.getPurchased().getDate());
assertEquals(panel.getPurchased().getMonth(), received.getPurchased().getMonth());
assertEquals(panel.getPurchased().getYear(), received.getPurchased().getYear());
assertEquals(panel.getCost(), received.getCost(),0.001);
assertEquals(panel.getSellingPrice(), received.getSellingPrice(),0.001);
assertEquals(panel.getUserId(), received.getUserId());
}
@After
public void tearDown() throws SQLException {
statement.executeUpdate(SqlQueries.DROP_PANEL_TABLE);
}
@AfterClass
public static void stopServer() {
server.shutdown();
}
}
oui je l'ai, je vais utiliser les requêtes préparées merci
Ce serait tellement plus facile à lire si vous n'avez tout simplement string concat au lieu de la classe StringBuilder, avec aucun changement dans la performance. La virgule et devis variables, il suffit de le rendre plus fort. append(virgule).append(devis) au lieu de append(",'"). Chaque ligne serait beaucoup plus lisible avec [+ "'" + panneau.getXXX() + "',"]
Ce serait tellement plus facile à lire si vous n'avez tout simplement string concat au lieu de la classe StringBuilder, avec aucun changement dans la performance. La virgule et devis variables, il suffit de le rendre plus fort. append(virgule).append(devis) au lieu de append(",'"). Chaque ligne serait beaucoup plus lisible avec [+ "'" + panneau.getXXX() + "',"]
OriginalL'auteur Tian Na | 2012-11-29
Vous devez vous connecter pour publier un commentaire.
Je voudrais utiliser une base de données en mémoire tels que H2, pour vérifier que votre SQL fonctionne réellement.
save
méthode, votre test doit appelersave
, puis sélectionnez la ligne à partir de la base de données et d'affirmer qu'il y a réellement quelque chose.find
méthode, votre test doit insérer des lignes dans la base de données directement, puis d'appelerfind
et d'affirmer que la ligne de votre choix ou de lignes ont été effectivement trouvés.c'est de très bons conseils. Mon expérience suggère que les différences entre SQL et ainsi de suite entre H2 et d'autres plates-formes sont assez mineures, surtout si vous choisissez le bon mode de compatibilité. Si il y a des bugs dans votre SQL, puis un test contre H2 trouverez, 99 fois sur 100. En revanche, l'exigence d'une "vraie" instance de base de données pour être mis en place rend votre JUnit ou TestNG tests un peu plus lourd, plus lent et plus difficile à gérer. Il y a des avantages et des inconvénients pour les deux approches, certes; cela peut être l'un de ceux "faire ce qui fonctionne pour vous.
avec votre phrase (Si il y a des bugs dans votre SQL, puis un test contre H2 trouverez, 99 fois sur 100.) il s'arrête en cours de test UNITAIRE. Pour être un bon test de l'unité que vous avez confiance en elle à chaque fois qu'il s'exécute.
Vous avez absolument raison. C'est pourquoi je n'ai pas appeler ça un test unitaire. Aussi, ce que je voulais dire, c'est que 99 sur 100 SQL bugs sera trouvé par un test qui utilise H2 - je ne voulais pas dire que le comportement de ces tests serait non-déterministe. L'un des avantages de l'utilisation d'une base de données en mémoire est que vous pouvez faire vos tests déterministes.
OriginalL'auteur Dawood ibn Kareem
Tout d'abord, vous ne devez pas créer des requêtes SQL par concaténation, parce qu'il est vulnérable à l'injection SQL. Utilisation
PreparedStatement
s à la place.En fait, il n'a pas beaucoup de sens pour tester DAO de cette façon. Vos tests seulement de vérifier que votre DAO transmet les valeurs de retour-et-vient correctement, mais il ne couvre pas la réelle complexité qui réside dans la justesse de requêtes SQL émis par votre DAO.
En d'autres termes, si vous voulez tester votre DAOs vous devez créer d'essai d'intégration, qui implique une vraie base de données. De cette façon, vous pouvez vérifier que les requêtes SQL émis par votre DAO sont corrects.
Standard moqueur techniques sont plus utiles dans le test de votre couche de Service (par exemple), où le code est-ce qui constitue difficile de logique, ou si vous avez besoin de créer des simulacres de tester vos classes en isolation (par exemple, vous voulez pour se moquer de votre DAOs). Généralement la plupart des DAOs contenir de code très simple, et les requêtes sont ce qui compose la plus grande partie de la complexité. Dans ce scénario traditionnel moqueur n'est pas très utile.
OriginalL'auteur axtavt
Je ne pense vraiment pas que la méthode de test est vraiment acheter quelque chose, et le code de test est extrêmement fragile. Je voudrais utiliser quelque chose comme DBUnit qui vous permet de "se moquer" de votre base de données. Ce sera effectivement vous permettre de tester l'exactitude de vos requêtes.
OriginalL'auteur ach