Comment tester les méthodes DAO en utilisant Mockito?
j'ai commencé à découvrir la bibliothèque Mockito et il y a une question pour laquelle je n'ai pas trouvé la bonne réponse.
si j'ai par exemple une telle méthode dans ma classe UserDAO qui sauve 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 le tester ?
si je veux tester par exemple une classe DAO, alors je dois créer un DataSource
faux, Connection
faux, ResultSet
faux, etc ? Et donc ne pas tester la base de données elle-même ?
Mais si je veux aussi tester l' comportement du dao et de la base de données ?
pourriez-vous s'il vous plaît produire quelques exemples de code, des liens qui pourraient être utiles et montrer les meilleures approches de le faire ?
4 réponses
voici un bon début en utilisant Mockito pour tester votre UserDAO. Ce code utilise une bonne partie des fonctionnalités de Mockito, de sorte que vous pouvez voir comment les utiliser. Laissez-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 tester la base de données (que vous devriez!), il n'y a aucun moyen de le contourner - Vous avez besoin d'une base de données réelle. Mockito, mais étant une grande bibliothèque, est probablement le mauvais outil pour ce travail.
Un outil comme DBUnit combiné avec JUnit pourrait vous aider à tester vos Oad 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 dans la base de données avec votre attente après le test.
Ici est de savoir comment vous devrait test:
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 remarques à propos de la ci-dessus:
1) les tests semblent courts, simples et faciles à comprendre, comme elles devraient être; s'ils semblent gros et laids comme ceux dans une autre réponse, vous faites quelque chose de fondamentalement mauvais.
2) le code de Test peut et doit avoir ses propres helpers d'infrastructure, tels que le IntegrationTests
classe de base, qui va cacher n'importe quel accès JDBC/ORM méchant de la réelle test. J'ai mis en place de tels helpers dans plusieurs projets, donc je sais que cela peut être fait, mais ce serait des choses pour d'autres questions.