简体   繁体   English

Java DAO实现测试

[英]Java DAO implementation testing

This is a very simple DAO attempt I'd like to share. 这是一个非常简单的DAO尝试,我想分享。

My question is if this is a correct way to test a DAO. 我的问题是,这是否是测试DAO的正确方法。 What I mean is that I'm verifying the SQL query and giving it return a mock. 我的意思是我正在验证SQL查询并给它返回一个模拟。 Then tell the mock to return these specific values and assert them? 然后告诉mock返回这些特定值并断言它们?

I have updated the DAO to use prepared statement instead of simple Statement. 我已经更新了DAO以使用预处理语句而不是简单的Statement。 Thanks. 谢谢。

public class PanelDao implements IO {

    private final static Logger LOGGER = Logger.getLogger(PanelDao.class);

    private Connection connection;

    public PanelDao() throws SQLException {
        this(MonetConnector.getConnection()); 
    }

    public PanelDao(Connection connection) throws SQLException {
        this.connection = connection;
    }

    @Override
    public void save(Panel panel) throws SQLException {
        final String query = "INSERT INTO panels VALUES ( ?, ?, ?, ?, ?, ?, ? )";
        final PreparedStatement statement = connection.prepareStatement(query);
        statement.setString(1, panel.getId());
        statement.setString(2, panel.getColor());
        statement.setDate(3, (new Date(panel.getPurchased().getTime())) );
        statement.setDouble(4, panel.getCost());
        statement.setDouble(5, panel.getSellingPrice());
        statement.setBoolean(6, panel.isOnSale());
        statement.setInt(7, panel.getUserId());

        LOGGER.info("Executing: "+query);
        statement.executeUpdate();
    }

    @Override
    public void update(Panel object) {
        throw new UnsupportedOperationException();      
    }

    @Override
    public void delete(Panel object) {
        throw new UnsupportedOperationException();      
    }

    @Override
    public Panel find(String id) throws SQLException {
        final String query = "SELECT * FROM panels WHERE id = ? ";
        final PreparedStatement statement = connection.prepareStatement(query);
        statement.setString(1, id);

        LOGGER.info("Executing: "+query);
        final ResultSet result = statement.executeQuery();

        final Panel panel = new Panel();
        if (result.next()) {
            panel.setId(result.getString("id"));
            panel.setColor(result.getString("color"));
        }
        return panel;       
    }
}

And the test class 和测试类

public class PanelDaoTest {

    @InjectMocks
    private PanelDao panelDao;

    @Mock 
    private Connection connection;

    @Mock
    private Statement statement;

    @Mock
    private ResultSet result;

    private Panel panel;

    @BeforeClass
    public static void beforeClass() {
        BasicConfigurator.configure();
    }

    @Before
     public void init() throws SQLException {
        MockitoAnnotations.initMocks(this);
        Mockito.when(connection.createStatement()).thenReturn(statement);
        panel = new Panel("AZ489", "Yellow", new Date(), 10.00, 7.50, true, 1);
    }

   @Test
   public void testSave() throws SQLException {
        Mockito.when(connection.prepareStatement("INSERT INTO panels VALUES ( ?, ?, ?, ?, ?, ?, ? )")).thenReturn(statement);
        panelDao.save(panel);
        Mockito.verify(statement).executeUpdate();
    }

    @Test
    public void testFind() throws SQLException {
        Mockito.when(connection.prepareStatement("SELECT * FROM panels WHERE id = ? ")).thenReturn(statement);
        Mockito.when(statement.executeQuery()).thenReturn(result);
        Mockito.when(result.next()).thenReturn(true);
        Mockito.when(result.getString("id")).thenReturn("AZ489");
        Mockito.when(result.getString("color")).thenReturn("Yellow");
        Panel panel = panelDao.find("AZ489");
        assertEquals("AZ489",panel.getId());
        assertEquals("Yellow",panel.getColor());
        Mockito.verify(statement).executeQuery();
     }
}

2.0 Testing DAO with HSQLDB 2.0使用HSQLDB测试DAO

After taking into account your feedback I decided to use HSQLDB for real database testing. 考虑到您的反馈后,我决定使用HSQLDB进行真正的数据库测试。 Please find this as a resource if tackling similar problems. 如果解决类似问题,请将此作为资源。

public class PanelDao implements IO {

    private final static Logger LOGGER = Logger.getLogger(PanelDao.class);

    private Connection connection;

    /**
     * Default constructor is using Monet connector
     */
    public PanelDao() throws SQLException {
        this(MonetConnector.getConnection()); 
    }

    public PanelDao(Connection connection) throws SQLException {
        this.connection = connection;
    }

    @Override
    public void save(Panel panel) throws SQLException {
        final String query = "INSERT INTO panels VALUES ( ?, ?, ?, ?, ?, ?, ? )";
        final PreparedStatement statement = connection.prepareStatement(query);
        statement.setString(1, panel.getId());
        statement.setString(2, panel.getColor());
        statement.setDate(3, (new Date(panel.getPurchased().getTime())) );
        statement.setDouble(4, panel.getCost());
        statement.setDouble(5, panel.getSellingPrice());
        statement.setBoolean(6, panel.isOnSale());
        statement.setInt(7, panel.getUserId());

        LOGGER.info("Executing: "+query);
        statement.executeUpdate();
    }

    @Override
    public void update(Panel object) {
        throw new UnsupportedOperationException();      
    }

    @Override
    public void delete(Panel object) {
        throw new UnsupportedOperationException();      
    }

    @Override
    public Panel find(String id) throws SQLException {
        final String query = "SELECT * FROM panels WHERE id = ? ";
        final PreparedStatement statement = connection.prepareStatement(query);
        statement.setString(1, id);

        LOGGER.info("Executing: "+query);
        final ResultSet result = statement.executeQuery();

        if (result.next()) {
            final Panel panel = new Panel();
            panel.setId(result.getString("id"));
            panel.setColor(result.getString("color"));
            panel.setPurchased(new Date(result.getDate("purchased").getTime()));
            panel.setCost(result.getDouble("cost"));
            panel.setSellingPrice(result.getDouble("selling_price"));
            panel.setOnSale(result.getBoolean("on_sale"));
            panel.setUserId(result.getInt("user_id"));
            return panel;
        }
        return null;        
    }
}

and the Test class: 和测试类:

public class PanelDaoTest {

    private PanelDao panelDao;
    private Panel panel;

    /* HSQLDB */
    private static Server server;
    private static Statement statement;
    private static Connection connection;

    @BeforeClass
    public static void beforeClass() throws SQLException {
        BasicConfigurator.configure();
        server = new Server();
        server.setAddress("127.0.0.1");
        server.setDatabaseName(0, "bbtest");
        server.setDatabasePath(0, ".");
        server.setPort(9000);
        server.start();
        PanelDaoTest.connection = DriverManager.getConnection("jdbc:hsqldb:hsql://127.0.0.1:9000/bbtest", "SA", "");
        PanelDaoTest.statement = PanelDaoTest.connection.createStatement();
    }

    @Before
    public void createDatabase() throws SQLException {
        PanelDaoTest.statement.execute(SqlQueries.CREATE_PANEL_TABLE);
        panelDao = new PanelDao(PanelDaoTest.connection);
    }

    @Test
    public void testSave() throws SQLException {
        panel = new Panel();
        panel.setId("A1");
        panel.setPurchased(new Date());
        panelDao.save(panel);
        assertNotNull(panelDao.find("A1"));
    }

    @Test
    public void testFind() throws SQLException {
        final String id = "45ZZE6";
        panel = Panel.getPanel(id);

        Panel received = panelDao.find(id);
        assertNull(received);

        panelDao.save(panel);
        received = panelDao.find(id);
        assertNotNull(received);
        assertEquals(panel.getId(), received.getId());
        assertEquals(panel.getColor(), received.getColor());
        assertEquals(panel.getPurchased().getDate(), received.getPurchased().getDate());
        assertEquals(panel.getPurchased().getMonth(), received.getPurchased().getMonth());
        assertEquals(panel.getPurchased().getYear(), received.getPurchased().getYear());
        assertEquals(panel.getCost(), received.getCost(),0.001);
        assertEquals(panel.getSellingPrice(), received.getSellingPrice(),0.001);
        assertEquals(panel.getUserId(), received.getUserId());
    }

    @After
    public void tearDown() throws SQLException {
        statement.executeUpdate(SqlQueries.DROP_PANEL_TABLE);
    }

    @AfterClass
    public static void stopServer() {
        server.shutdown();
    }
}

First of all, you should not create SQL queries by concatenation, because it's vulnerable to SQL injection. 首先,您不应该通过连接创建SQL查询,因为它易受SQL注入攻击。 Use PreparedStatement s instead. 请改用PreparedStatement

Actually, it doesn't make much sense to test DAO this way. 实际上,以这种方式测试DAO没有多大意义。 Your tests only verify that your DAO passes values back and forth correctly, but it doesn't cover the real complexity that lies in correctness of SQL queries issued by your DAO. 您的测试仅验证您的DAO是否正确地来回传递值,但它并未涵盖DAO发出的SQL查询正确性的真正复杂性。

In other words, if you want to test your DAOs you need to create integration test that involves real database. 换句话说,如果要测试DAO,则需要创建涉及真实数据库的集成测试。 This way you can verify that SQL queries issued by your DAO are correct. 这样,您可以验证DAO发出的SQL查询是否正确。

I don't really think that method of testing is really buying you anything, and the test code is extremely brittle. 我真的不认为测试方法真的会给你买任何东西,测试代码非常脆弱。 I would use something like DBUnit that allows you to "mock" your database. 我会使用像DBUnit这样的东西来“模拟”你的数据库。 This will actually allow you to test the correctness of your queries. 这实际上允许您测试查询的正确性。

I would use an in-memory database such as H2, to test that your SQL actually works. 我会使用像H2这样的内存数据库来测试你的SQL实际工作。

  • When you test your save method, your test should call save , then select the row from the database and assert that there is actually something there. 当你测试你的save方法时,你的测试应该调用save ,然后从数据库中选择行并断言那里确实有一些东西。
  • When you test your find method, your test should insert some rows in the database directly, then call find and assert that the desired row or rows were actually found. 当您测试find方法时,您的测试应该直接在数据库中插入一些行,然后调用find并断言实际找到了所需的行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM