J'ai une méthode DAO qui utilise Spring pour l'accès JDBC. Il calcule le taux de réussite d'un vendeur à vendre un article.
Voici le code:
public BigDecimal getSellingSuccessRate(long seller_id) {
String sql = "SELECT SUM(IF(sold_price IS NOT NULL, 1, 0))/SUM(1)
FROM transaction WHERE seller_id = ?";
Object[] args = {seller_id};
return getJdbcTemplate().queryForObject(sql, args, BigDecimal.class);
}
Comment dois-je procéder pour tester cette méthode ou toute autre méthode DAO avec JUnit? Quelles sont les meilleures pratiques pour tester la logique d'accès aux données? Je pense à le tester par rapport à une base de données embarquable chargée de certaines données, mais ne devrions-nous pas faire des tests d'intégration similaires à un environnement de production en termes de SGBDR et de schéma?
Le problème avec l'utilisation d'une "vraie" base de données pour les tests unitaires est la configuration, la suppression et l'isolement des tests. Vous ne voulez pas avoir à créer une base de données MySQL entièrement nouvelle et à créer des tables et des données juste pour un test unitaire. Les problèmes avec cela ont à voir avec la nature externe de la base de données et votre base de données de test est en panne, vos tests unitaires échouent. Il y a également des problèmes à vérifier que vous disposez d'une base de données unique pour les tests. Ils peuvent être surmontés, mais il existe une réponse plus simple.
Se moquer de la base de données est une option cependant il ne teste pas les requêtes réelles qui sont exécutées. Il peut être utilisé comme une solution beaucoup plus simple lorsque vous voulez vous assurer que les données du DAO passent correctement par le système. Mais pour tester le DAO lui-même, vous avez besoin de quelque chose derrière le DAO pour que les données et les requêtes s'exécutent correctement.
La première chose à faire est d'utiliser une base de données en mémoire. HyperSQL est un excellent choix pour cela car il a la capacité d'émuler le dialecte d'une autre base de données - de sorte que les différences mineures entre les bases de données restent les mêmes (types de données, fonctions, etc.). hsqldb a également quelques fonctionnalités intéressantes pour les tests unitaires.
db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;
Cela charge l'état de la base de données (les tables, les données initiales) à partir du fichier testData
. shutdown=true
fermera automatiquement la base de données à la fermeture de la dernière connexion.
En utilisant injection de dépendance , demandez aux tests unitaires de sélectionner une base de données différente que celle utilisée par les builds de production (ou test, ou local) .
Votre DAO utilise ensuite la base de données injectée pour laquelle vous pouvez lancer des tests sur la base de données.
Les tests unitaires ressembleront alors à quelque chose (tas de trucs ennuyeux non inclus pour plus de brièveté):
@Before
public void setUpDB() {
DBConnection connection = new DBConnection();
try {
conn = connection.getDBConnection();
insert = conn.prepareStatement("INSERT INTO data (txt, ts, active) VALUES (?, ?, ?)");
} catch (SQLException e) {
e.printStackTrace();
fail("Error instantiating database table: " + e.getMessage());
}
}
@After
public void tearDown() {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void addData(String txt, Timestamp ts, boolean active) throws Exception {
insert.setString(1, txt);
insert.setTimestamp(2, ts);
insert.setBoolean(3, active);
insert.execute();
}
@Test
public void testGetData() throws Exception {
// load data
Calendar time = Calendar.getInstance();
long now = time.getTimeInMillis();
long then1h = now - (60 * 60 * 1000); // one hour ago
long then2m = now - (60 * 1000 * 2); // two minutes ago
addData("active_foo", new Timestamp(then1h), true); // active but old
addData("inactive_bar", new Timestamp(then1h), false); // inactive and old
addData("active_quz", new Timestamp(then2m), true); // active and new
addData("inactive_baz", new Timestamp(then2m), false); // inactive and new
DataAccess dao = new DataAccess();
int count = 0;
for (Data data : dao.getData()) {
count++;
assertTrue(data.getTxt().startsWith("active"));
}
assertEquals("got back " + count + " rows instead of 1", count, 1);
}
Et donc, vous avez un test unitaire qui appelle le DAO et utilise les données qui ont été configurées dans une base de données à la volée qui existe pour la durée du test. Vous n'avez pas à vous soucier des ressources externes ou de l'état de la base de données avant l'exécution, ou de la restauration à un état connu (enfin, "l'état connu" est "n'existe pas", ce qui est trivial pour revenir à).
DBUnit peut faire beaucoup de ce que j'ai décrit un processus plus simple dans la configuration de la base de données, la création des tables et le chargement des données. Si vous deviez utiliser la base de données réelle pour une raison quelconque, c'est de loin le meilleur outil à utiliser.
Le code ci-dessus fait partie d'un projet maven que j'ai écrit pour la preuve de concept TestingWithHsqldb sur github
Tout d'abord, vous ne devez jamais effectuer de tests dans un environnement de production. Vous devez disposer d'un environnement de test qui reflète votre environnement de production et y effectuer des tests d'intégration.
Si vous faites cela, vous pouvez faire un certain nombre de choses.
Sur notre projet, chaque développeur exécute une base de données vide, sa structure est la même que la base de données de production.
Dans chaque test unitaire TestInitialize, nous créons une connexion et une transaction à la base de données ainsi que certains objets par défaut dont nous avons besoin pour chaque test. Et tout est annulé après la fin de chaque méthode ou classe.
De cette façon, il est possible de tester la couche sql. En fait, chaque requête ou appel de base de données doit être testé de cette manière.
L'inconvénient est qu'il est lent, nous l'avons donc mis dans un projet distinct de nos tests unitaires réguliers. Il est possible d'accélérer cela en utilisant une base de données en mémoire, mais l'idée reste la même.