web-dev-qa-db-fra.com

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?

26
marknorkin

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;
        }

    }
}
23
Jose Martinez

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.

5
Mureinik

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.

3
Rogério

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.

1
pommes