How to test DAO methods using Mockito?

后端 未结 4 632
抹茶落季
抹茶落季 2021-01-31 09:24

I\'ve started to discovered Mockito library and there is a question for which I didn\'t find the proper answer.

If I have for example such method in my UserDAO class tha

相关标签:
4条回答
  • 2021-01-31 09:28

    A tool like DBUnit combined with JUnit could help you testing your DAOs with the database. DBUnit helps you inserting test data to the database before your UnitTest and compare the data in the database with your expectation after the test.

    0 讨论(0)
  • 2021-01-31 09:34

    Here is a good start using Mockito to test your UserDAO. This code uses a good amount of the Mockito features, so you can see how to use them. Let me know if you have 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;
            }
    
        }
    }
    
    0 讨论(0)
  • 2021-01-31 09:37

    But what if I want to also test the behavior of dao and database ?

    If you indeed want to test the database (as you should!), there's no way around it - you need an actual database. Mockito, albeit being a great library, is probably the wrong tool for this job.

    0 讨论(0)
  • 2021-01-31 09:43

    Here is how you should test it:

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

    A couple notes about the above:

    1) The tests look short, simple, and easy to understand, as they should be; if they look big and ugly as those in another answer, you are doing something fundamentally wrong.

    2) Test code can and should have its own infrastructure helpers, such as the IntegrationTests base class, which will hide any nasty JDBC/ORM access from the actual tests. I implemented such helpers in several projects, so I know this can be done, but that would be stuff for other questions.

    0 讨论(0)
提交回复
热议问题