J'aimerais écrire des tests unitaires pour du code qui se connecte à une base de données, exécute une ou plusieurs requêtes, puis traite les résultats . (Sans utiliser réellement de base de données)
Un autre développeur a écrit ici notre propre implémentation DataSource, Connection, Statement, PreparedStatement et ResultSet qui renverra les objets correspondants en fonction d'un fichier de configuration xml. (nous pourrions utiliser la source de données fictive et simplement exécuter des tests sur les ensembles de résultats renvoyés).
Sommes-nous réinventer la roue ici? Existe-t-il déjà quelque chose comme ceci pour les tests unitaires? Existe-t-il d'autres/meilleurs moyens de tester le code jdbc?
Vous avez plusieurs options:
Il est parfois nécessaire et utile de rendre ces tests configurables afin que ces tests ne soient exécutés que si la base de données est disponible. Cela peut être fait avec par exemple construire des propriétés.
J'aime utiliser une combinaison de:
Vous pouvez aller assez loin avec seulement DBUnit et HSQLDB. Unitils fournit le dernier kilomètre de code pour gérer et réinitialiser l'état de la base de données. Il fournit également un moyen pratique de gérer les modifications de schéma de base de données et facilite l’utilisation de RBDMS spécifiques (Oracle, DB2, SQL Server, etc.). Enfin, Unitils fournit quelques wrappers Nice autour de DBUnit, qui modernisent l’API et rendent DBUnit bien plus agréable à utiliser.
Si vous n'avez pas encore vérifié Unitils, vous devriez le faire. Unitils est souvent négligé et sous-estimé.
Je préfère utiliser EasyMock pour tester un code pas-si-facile-à-tester.
Je dirais que HSQL est la voie à suivre lors de vos tests unitaires. Le but de votre test est de tester votre code jdbc et de vous assurer qu'il fonctionne. Ajouter des classes personnalisées ou se moquer des appels jdbc peut facilement cacher des bugs.
J'utilise principalement mysql et lorsque les tests exécutent la classe de pilote et que l'URL est modifiée en org.hsqldb.jdbcDriver et jdbc: hsqldb: mem: test.
Si vous souhaitez effectuer des tests unitaires et non des tests d'intégration, vous pouvez utiliser une approche très basique et simple, utilisant uniquement Mockito, comme ceci:
public class JDBCLowLevelTest {
private TestedClass tested;
private Connection connection;
private static Driver driver;
@BeforeClass
public static void setUpClass() throws Exception {
// (Optional) Print DriverManager logs to system out
DriverManager.setLogWriter(new PrintWriter((System.out)));
// (Optional) Sometimes you need to get rid of a driver (e.g JDBC-ODBC Bridge)
Driver configuredDriver = DriverManager.getDriver("jdbc:odbc:url");
System.out.println("De-registering the configured driver: " + configuredDriver);
DriverManager.deregisterDriver(configuredDriver);
// Register the mocked driver
driver = mock(Driver.class);
System.out.println("Registering the mock driver: " + driver);
DriverManager.registerDriver(driver);
}
@AfterClass
public static void tearDown() throws Exception {
// Let's cleanup the global state
System.out.println("De-registering the mock driver: " + driver);
DriverManager.deregisterDriver(driver);
}
@Before
public void setUp() throws Exception {
// given
tested = new TestedClass();
connection = mock(Connection.class);
given(driver.acceptsURL(anyString())).willReturn(true);
given(driver.connect(anyString(), Matchers.<Properties>any()))
.willReturn(connection);
given(connection.prepareCall(anyString())).willReturn(statement);
}
}
Ensuite, vous pouvez tester différents scénarios, comme dans tout autre test Mockito, par exemple.
@Test
public void shouldHandleDoubleException() throws Exception {
// given
SomeData someData = new SomeData();
given(connection.prepareCall(anyString()))
.willThrow(new SQLException("Prepare call"));
willThrow(new SQLException("Close exception")).given(connection).close();
// when
SomeResponse response = testClass.someMethod(someData);
// then
assertThat(response, is(SOME_ERROR));
}
Bien que la manière de se moquer de jdbc dans votre application dépende, bien sûr, de la manière dont vous avez implémenté vos transactions jdbc réelles.
Si vous utilisez jdbc en l'état, je suppose que vous avez écrit vous-même une classe d'utilitaires pour effectuer certaines tâches dans la ligne de DBUtils.getMetadataFor(String tablename)
. Cela signifie que vous devez créer une maquette de cette classe et que cela pourrait être tout ce dont vous avez besoin. Ce serait une solution assez facile pour vous puisque vous avez apparemment déjà une série d'objets fictifs liés à jdbc. Notez que je suppose que votre code jdbc n’est pas explosé tout autour de l’application - si c’est le cas, refactor !!!
Si vous utilisez toutefois un cadre pour la gestion de base de données (comme les classes de modèle JDBC de Spring Framework), vous pouvez et devriez vous moquer de la classe d'interface en utilisant EasyMock ou un autre équivalent. De cette façon, vous pouvez disposer de toute la puissance nécessaire pour vous permettre de vous moquer de la connexion.
Enfin, si rien ne fonctionne, vous pouvez faire ce que d’autres ont déjà dit et utiliser DBUnit et/ou derby.
Il y a DBUnit . Cela ne vous permettra pas de tester votre code jdbc sans base de données, mais il semble que vous puissiez introduire un ensemble différent d’achats en émulant une base de données.
Le pilote Acolyte peut être utilisé pour créer une connexion JDBC, la gérer pendant les tests et renvoyer les données sous forme de jeu de résultats (avec son API de liste de lignes dactylographiée): https://github.com/cchantep/acolyte
Note: Je suis l'auteur de Acolyte.
Jetez un coup d'œil à JDBDT: http://jdbdt.org
Vous pouvez l'utiliser pour la configuration de la base de données et des assertions, au lieu de DBUnit.
Note: Je suis l'auteur de JDBDT.
Nous utilisons Mockrunner. http://mockrunner.sourceforge.net/ Il possède des connexions fictives et des sources de données intégrées, il n'est donc pas nécessaire de les implémenter vous-même.