java - 使用 JUnit 在 Spring 中测试 CRUD 时获取同一行的多个 ID

标签 java spring unit-testing junit

我将 JUnit4 与 AbstractTransactionalJUnit4SpringContextTests 结合使用,并希望创建简单的 CRUD 单元测试 - 如下所示。但是,对 SQL 更新的调用导致事务管理器为我的测试实例生成一个新 ID。由于在更新期间没有传回新 ID,因此我不再拥有我正在测试的行的主键 - 这会阻碍测试的其余部分。

有没有办法阻止事务管理器在调用更新时为图书生成新的 ID? (如果失败,是否有更好的方法来测试 CRUD?)

@Test
public void testCRUDBook() {
    Book b1 = new Book(title, author);
    BookFactory factory = database.getBookFactory();
    int id = factory.createBook(b1);

    Book b2 = factory.readBook(id);        
    assertEquals(b1.getTitle(), b2.getTitle());
    assertEquals(b1.getAuthor(), b2.getAuthor());

    b2.setTitle("title 2");
    b2.setAuthor("author 2");
    assertTrue(factory.updateBook(b2));

    // The problem arises here as updating the book record causes a new Id 
    // to be generated so querying by Id is no longer possible.
    Book b3 = factory.readBook(b2.getId());        
    assertEquals(b3.getTitle(), "title 2");
    assertEquals(b3.getAuthor(), "author 2");    

    assertTrue(factory.deleteBook(b3));
}

这本书看起来像这样:

public class Book {
    private int id;
    private String title;
    private String author;

    public Book() {}

    // NEW
    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    // READ
    public Book(int id, String title, String author) {
        this.id = id;
        this.title = title;
        this.author = author;
    }

    // GENERIC ACCESSORS (left out for brevity)
}

为了完整性 - 工厂:

public class BookFactory extends BaseDatabaseFactory {

        @Transactional
    public int createBook(final Book b) {
        final String insertSQL = "INSERT INTO book (author, title) VALUES (?, ?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        try {
            int update = jdbcTemplate.update(
                    new PreparedStatementCreator() {
                        @Override
                        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                            PreparedStatement ps = connection.prepareStatement(insertSQL, PreparedStatement.RETURN_GENERATED_KEYS);
                            ps.setString(1, b.getTitle());
                            ps.setString(2, b.getAuthor());
                            return ps;
                        }
                    },
                    keyHolder);
            if (update != 1) {
                throw new IllegalStateException("Adding book record to database resulted in " + update + " records.");
            }
            return keyHolder.getKey().intValue();
        } catch (DataAccessException ex) {
            String msg = "Falied to create new book:" + b.toString() + "ex:" + ex.getMessage();
            throw new RuntimeException(msg);
        }
    }

    private static class BookRowMapper implements RowMapper {
        @Override
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Book b = new Book();
            b.setId(rs.getInt("id"));
            b.setTitle(rs.getString("title"));
            b.setAuthor(rs.getString("author"));
            return b;
        }
    }

    @Transactional
    public Book readBook(int id) {
        Book b = null;
        try {
            String sql = "SELECT * FROM book WHERE id = " + id;
            b = (Book) jdbcTemplate.queryForObject(sql, new Object[]{}, new BookRowMapper());
        } catch (DataAccessException ex) {
            throw new RuntimeException("Falied to locate book.", ex);
        }
        return b;
    }    

    @Transactional
    public boolean updateBook(final Book b) {
        final String updateSQL = "UPDATE book SET author = ?, title = ? WHERE id = ?";
        try {
            jdbcTemplate.update(
                    new PreparedStatementCreator() {
                        @Override
                        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                            PreparedStatement ps = connection.prepareStatement(updateSQL);
                            ps.setString(1, b.getAuthor());
                            ps.setString(2, b.getTitle());
                            ps.setInt(3, b.getId());
                            return ps;
                        }
                    });
        } catch (DataAccessException ex) {
            log.error("Falied to update Book:" + b.toString(), ex);
            return false;
        }
        return true;
    }

    @Transactional
    public boolean deleteBook(final Book b) {
        try {
            jdbcTemplate.update("DELETE FROM book WHERE id = ?", b.getId());
        } catch (DataAccessException ex) {
            log.error("Falied to delete Book:" + b.toString(), ex);
            return false;
        }
        return true;
    }    
}

最佳答案

您可以将 id 改成 Integer 类型吗?当您对 b2 进行更新时,看起来好像工厂会导致创建一个新实例。 id 应该可以为空。

关于java - 使用 JUnit 在 Spring 中测试 CRUD 时获取同一行的多个 ID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11441640/

相关文章:

java - jpa @manytoone 创建复合主键而不是 @id

java - 测试类中的模拟接口(interface)

unit-testing - 如何使用 rabbitmq 在 python 中编写单元/集成测试?

unit-testing - 难以进行单元测试的方法类型?

java - 如何从 Swing 与 Servlet 进行通信

python - Django REST Framework 缓存错误

java - 如果存在我的自定义注释并且 RestTemplate 正在使用对象映射器,则不会调用 AnnotationIntrospector

java.lang.NullPointerException 错误

java - while(rs.next()) 不返回所有记录,只返回最后一条

java - 必须单击 "Submit"按钮两次才能提交表单