Avere un componente che rappresenta il database è meraviglioso! Puoi usarlo dalla logica di business per leggere e scrivere dati. Ma questo componente dovrebbe essere testato unitamente?
Direi che non dovrebbe. I test unitari riguardano esclusivamente la verifica del contratto di un componente con i suoi attori esterni.
Prendiamo un'interfaccia tipica di un componente del database
void insertUsers(List<UserRecord> users);
List<UserRecord> fetchUsers(List<UserID> userIds);
void deleteUsers(List<UserID> userIds);
Ogni metodo elencato utilizza la libreria del database per inviare le query appropriate al database.
Gli attori esterni del componente del database sono:
- La logica aziendale
- Il database
Il test delle unità mi richiederebbe di prendere in giro la connessione, l'istruzione preparata e gli oggetti del set di risultati della libreria del database.
Tuttavia, poiché l'intero componente del database consiste solo di interazioni con la libreria del database, qualsiasi test che scrivo finirà per rispecchiare il codice del componente ed è quindi fragile.
Verificare il contratto significa affermare quanto segue:
- Chiamare insertUsers dovrebbe scrivere quegli utenti nel database
- Chiamare fetchUsers dovrebbe recuperare quegli utenti dal database
- Chiamare deleteUsers dovrebbe eliminare quegli utenti dal database
Il test dell'interazione con la libreria del database porterà a un codice fragile:
- È possibile creare molte istruzioni SQL equivalenti. La modifica di una dichiarazione in una equivalente non interrompe il contratto e non dovrebbe interrompere il test
- Si potrebbe usare la libreria del database in diversi modi: ad esempio: utilizzare una dichiarazione invece di un oggetto istruzione preparato. Anche questo non rompe il contratto
- La modifica dell'ordine delle colonne in un'istruzione select o insert produrrebbe risultati equivalenti. Assenza di affermazione su resultset.getString (0) o preparedStatement.setString (1, "Bill")
- La libreria di database utilizzata non dovrebbe avere importanza.
Le seguenti considerazioni mi hanno portato alla conclusione che i test unitari per il componente del database offrono poco valore. Sento davvero che un test di integrazione che richiede un vero database è la strada da percorrere.
Per favore condividi le tue opinioni sull'argomento; Potrebbe essere che mi manca qualcosa?
Modifica: Si prega di suggerire in che modo il seguente codice può essere testato unitamente in modo non fragile. Sentiti libero di refactoring il codice se ti piace.
void insertUsers(List<UserRecord> users) throws RepositoryException{
try(Connection connection = datasource.getConnection();
PreparedStatement stmt = connection.prepareStatement("insert into users (Name, Surname, DateOfBirth) values (?,?,?)")){
for (UserRecord user : users){
stmt.setString(1, user.name);
stmt.setString(2, user.surname);
stmt.setTimestamp(3, user.dateOfBirth);
stmt.execute();
}
} catch(SQLException ex){
throw new RepositoryException(ex);
}
}