Comment tester les méthodes DAO avec Mockito?
J'ai commencé à découvrir la bibliothèque Mockito et il y a une question à laquelle je n'ai pas trouvé de réponse satisfaisante.
Si j'ai par exemple une telle méthode dans ma classe UserDAO qui enregistre l'utilisateur dans la base de données:
public class UserDAO{
...
public void create(User user) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet generatedKeys = null;
try {
connection = getConnection();
pstmt = connection.prepareStatement(INSERT_USER,
PreparedStatement.RETURN_GENERATED_KEYS);
int counter = 1;
pstmt.setString(counter++, user.getFirstName());
pstmt.setString(counter++, user.getLastName());
pstmt.setString(counter++, user.getEmail());
pstmt.setString(counter++, user.getPassword());
pstmt.setString(counter++, user.getRole());
pstmt.setString(counter, user.getLang());
pstmt.execute();
connection.commit();
generatedKeys = pstmt.getGeneratedKeys();
if (generatedKeys.next()) {
user.setId(generatedKeys.getInt(Fields.GENERATED_KEY));
}
} catch (SQLException e) {
rollback(connection);
LOG.error("Can not create a user", e);
} finally {
close(connection);
close(pstmt);
close(generatedKeys);
}
}
....
}
Comment devrais-je le tester?
Si je veux tester par exemple une classe DAO, je dois créer un DataSource
mock, Connection
mock, ResultSet
mock, etc.? Et donc ne pas tester la base de données elle-même?
Mais que faire si je veux aussi tester le comportement de Dao et de la base de données?
Pourriez-vous s'il vous plaît produire des exemples de code, des liens qui pourraient être utiles et montrer les meilleures méthodes pour le faire?
Voici un bon début d’utilisation de Mockito pour tester votre UserDAO. Ce code utilise une bonne partie des fonctionnalités de Mockito afin que vous puissiez voir comment les utiliser. Faites moi savoir si vous avez des questions.
import Java.sql.Connection;
import Java.sql.PreparedStatement;
import Java.sql.ResultSet;
import Java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import org.mockito.Mock;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class TestUserDAO {
@Mock
DataSource mockDataSource;
@Mock
Connection mockConn;
@Mock
PreparedStatement mockPreparedStmnt;
@Mock
ResultSet mockResultSet;
int userId = 100;
public TestUserDAO() {
}
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() {
}
@Before
public void setUp() throws SQLException {
when(mockDataSource.getConnection()).thenReturn(mockConn);
when(mockDataSource.getConnection(anyString(), anyString())).thenReturn(mockConn);
doNothing().when(mockConn).commit();
when(mockConn.prepareStatement(anyString(), anyInt())).thenReturn(mockPreparedStmnt);
doNothing().when(mockPreparedStmnt).setString(anyInt(), anyString());
when(mockPreparedStmnt.execute()).thenReturn(Boolean.TRUE);
when(mockPreparedStmnt.getGeneratedKeys()).thenReturn(mockResultSet);
when(mockResultSet.next()).thenReturn(Boolean.TRUE, Boolean.FALSE);
when(mockResultSet.getInt(Fields.GENERATED_KEYS)).thenReturn(userId);
}
@After
public void tearDown() {
}
@Test
public void testCreateWithNoExceptions() throws SQLException {
UserDAO instance = new UserDAO(mockDataSource);
instance.create(new User());
//verify and assert
verify(mockConn, times(1)).prepareStatement(anyString(), anyInt());
verify(mockPreparedStmnt, times(6)).setString(anyInt(), anyString());
verify(mockPreparedStmnt, times(1)).execute();
verify(mockConn, times(1)).commit();
verify(mockResultSet, times(2)).next();
verify(mockResultSet, times(1)).getInt(Fields.GENERATED_KEYS);
}
@Test(expected = SQLException.class)
public void testCreateWithPreparedStmntException() throws SQLException {
//mock
when(mockConn.prepareStatement(anyString(), anyInt())).thenThrow(new SQLException());
try {
UserDAO instance = new UserDAO(mockDataSource);
instance.create(new User());
} catch (SQLException se) {
//verify and assert
verify(mockConn, times(1)).prepareStatement(anyString(), anyInt());
verify(mockPreparedStmnt, times(0)).setString(anyInt(), anyString());
verify(mockPreparedStmnt, times(0)).execute();
verify(mockConn, times(0)).commit();
verify(mockResultSet, times(0)).next();
verify(mockResultSet, times(0)).getInt(Fields.GENERATED_KEYS);
throw se;
}
}
}
Mais que faire si je veux aussi tester le comportement de Dao et de la base de données?
Si vous voulez vraiment tester la base de données (comme vous le devriez!), Il n’ya aucun moyen de la contourner - vous avez besoin d’une base de données réelle. Mockito, bien qu’étant une excellente bibliothèque, n’est probablement pas le bon outil pour ce travail.
Voici comment vous devriez le tester:
public class UserDAOTest extends IntegrationTests
{
// Or do it in a @Before method, if needed.
UserDAO dao = new UserDAO();
@Test
public void createValidUser() {
User validUser = new User(
"John", "Smith", "johns@gmail.com", "Abc123!@",
"admin", "en"); // or use setters as needed
dao.create(validUser);
assertEntityCreatedInDB(validUser);
}
@Test
public void attemptToCreateInvalidUser() {
User invalidUser = new User("", null, null, "", null, "XY");
dao.create(invalidUser);
// This really shouldn't be done this way, as DAOs are not supposed
// to manage transactions; instead, a suitable, descriptive
// exception should be thrown by the DAO and checked in the test.
assertTransactionWasRolledBack();
}
}
Quelques notes sur ce qui précède:
1) Les tests semblent courts, simples et faciles à comprendre, car ils devraient l'être; S'ils semblent gros et laids comme ceux d'une autre réponse, vous faites quelque chose de fondamentalement faux.
2) Le code de test peut et devrait avoir ses propres assistants d’infrastructure, tels que la classe de base IntegrationTests
, qui cachera tout accès JDBC/ORM désagréable des tests réels. J'ai implémenté de tels assistants dans plusieurs projets, donc je sais que cela peut être fait, mais ce serait une matière pour d'autres questions.
Un outil tel que DBUnit associé à JUnit peut vous aider à tester vos DAO avec la base de données. DBUnit vous aide à insérer des données de test dans la base de données avant votre UnitTest et à comparer les données de la base de données avec vos attentes après le test.