Java 如何使用 Mockito 测试 DAO 方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28388204/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 06:08:06  来源:igfitidea点击:

How to test DAO methods using Mockito?

javaunit-testingjunitmockitodao

提问by marknorkin

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

我已经开始发现 Mockito 库,但有一个问题我没有找到正确的答案。

If I have for example such method in my UserDAO class that saves user in database:

例如,如果我的 UserDAO 类中有这样的方法可以将用户保存在数据库中:

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

How should I test it ?

我应该如何测试它?

If I want to test for example a DAO class then I need to create a DataSourcemock, Connectionmock, ResultSetmock etc ? And so not to test the database itself ?

如果我想测试例如一个 DAO 类,那么我需要创建一个DataSource模拟、Connection模拟、ResultSet模拟等?所以不测试数据库本身?

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

但是如果我还想测试 dao 和 database 的行为怎么办?

Would you please produce some code samples, links that could be helpful and show best approaches of doing it ?

您能否制作一些代码示例、可能有用的链接并展示最佳方法?

采纳答案by Jose Martinez

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.

这是使用 Mockito 测试您的 UserDAO 的良好开端。此代码使用了大量 Mockito 功能,因此您可以了解如何使用它们。如果您有任何疑问,请告诉我。

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

    }
}

回答by Mureinik

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

但是如果我还想测试 dao 和 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.

如果您确实想测试数据库(正如您应该的那样!),那就没有办法了 - 您需要一个实际的数据库。Mockito 虽然是一个很棒的库,但可能是完成这项工作的错误工具。

回答by pommes

A tool like DBUnitcombined 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.

DBUnit 这样结合 JUnit 的工具可以帮助您使用数据库测试 DAO。DBUnit 帮助您在 UnitTest 之前将测试数据插入数据库,并在测试之后将数据库中的数据与您的期望进行比较。

回答by Rogério

Here is how you shouldtest 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", "[email protected]", "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.

1) 测试看起来简短、简单且易于理解,这是应该的;如果它们看起来像另一个答案中的那样又大又丑,那你就做错了。

2) Test code can and should have its own infrastructure helpers, such as the IntegrationTestsbase 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.

2) 测试代码可以而且应该有自己的基础设施助手,例如IntegrationTests基类,它会在实际测试中隐藏任何讨厌的 JDBC/ORM 访问。我在几个项目中实现了这样的助手,所以我知道这是可以做到的,但这将是其他问题的东西。