PreparedStatement.execute()在Spring Unit Test中挂起

时间:2011-10-04 18:30:30

标签: unit-testing spring-mvc jdbc

我在Spring MVC应用程序中的单元测试遇到了一些麻烦。在完全公开的情况下,由于缺乏从头开始编写测试套件的经验,我很有可能错误地设计我的单元测试。

我目前设计的方式是测试用户服务,测试套件使用原始SQL语句来验证数据是否正确插入/检索/更新。我遇到的问题是,在执行frist预处理语句之后,后续语句会挂起execute()方法。测试结果最终为“超出锁定等待超时;尝试重新启动事务”

根据我在网上看到的内容,这可能是一个交易管理问题,有人没有发布锁定,但我不确定如何最好地做到这一点,甚至不知道在哪里做。

下面是一些相关代码,如果需要更多代码,请告诉我。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext-base.xml", "/application-security.xml"})
@TransactionConfiguration(transactionManager="txManager")
@Transactional
public class TestUserService {

    @Autowired
    UsersService userService;

    @Autowired
    DataSource dataSource;

    Connection connection;

    @Before
    public void setup() throws Exception{
        connection = dataSource.getConnection();
    }

    @Test
    public void testCreateUser() throws Exception{

        Collection<GrantedAuthorityImpl> auths = new ArrayList<GrantedAuthorityImpl>();
        auths.add(new GrantedAuthorityImpl(SecurityConstants.ROLE_USER));


        User user = new User("testUser", "testpassword", true, true, true, true, auths, "salt");

        User tmp = userService.createUser(user);


        PreparedStatement ps = connection.prepareStatement("select id, username, password, created, enabled, salt from users where id = ?");
        PreparedStatement ps2 = connection.prepareStatement("select user, authority from user_authorities where user = ?");

        ps.setLong(1, tmp.getId());
        ps2.setLong(1, tmp.getId());

        ResultSet rs = ps.executeQuery();
        ResultSet rs2 = ps2.executeQuery();

        rs.first();
        rs2.first();

        Collection<GrantedAuthorityImpl> authsFromDb = new ArrayList<GrantedAuthorityImpl>();

        rs.first();
        do{
            authsFromDb.add(new GrantedAuthorityImpl(rs2.getString("authority")));
        }while(rs2.next());

        User tmp2 = new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("enabled"), true, true, true, authsFromDb, rs.getString("salt"));

        Assert.assertEquals(tmp.getUsername(), tmp2.getUsername());
        Assert.assertEquals(tmp.getId(), tmp2.getId());
        Assert.assertEquals(tmp.getPassword(), tmp2.getPassword());
        Assert.assertEquals(tmp.getSalt(), tmp2.getSalt());
        Assert.assertEquals(tmp.getAuthorities(), tmp2.getAuthorities());
        Assert.assertEquals(tmp.isEnabled(), tmp2.isEnabled());

    }

    @Test
    public void testSaveUser() throws Exception{
        long createdTime = System.currentTimeMillis();
        String insertionQry = "insert into users (username, password, created, enabled, salt) values ('chris', 'somepassword'," + createdTime + ",1,'salt')";


        PreparedStatement ps = connection.prepareStatement(insertionQry, Statement.RETURN_GENERATED_KEYS);
        ps.execute();
        ResultSet rs = ps.getGeneratedKeys();
        rs.first();
        long id = rs.getLong(1);

        Assert.assertEquals(true, id != 0);

        String loadQry = "select id, username, password, created, enabled, salt from users where id = " + id;

        ps = connection.prepareStatement(loadQry);
        rs = ps.executeQuery();

        rs.first();

        Assert.assertEquals(rs.getString("username"), "chris");
        Assert.assertEquals(rs.getString("password"), "somepassword");
        Assert.assertEquals(rs.getBoolean("enabled"), true);
        Assert.assertEquals(rs.getString("salt"), "salt");


        User user = new User("second_username", "newpassword", false, true, true, true, AuthorityUtils.NO_AUTHORITIES, "secondsalt");
        user.setId(rs.getLong("id"));

        userService.saveUser(user);

        ps = connection.prepareStatement(loadQry);
        rs = ps.executeQuery();

        rs.first();

        Assert.assertEquals(rs.getString("username"), "second_username");
        Assert.assertEquals(rs.getString("password"), "newpassword");
        Assert.assertEquals(rs.getBoolean("enabled"), false);
        Assert.assertEquals(rs.getString("salt"), "secondsalt");



    }

2 个答案:

答案 0 :(得分:3)

为了在Spring事务管理中使用原始JDBC Connection,您需要将其作为DataSourceUtils.getConnection(dataSource)获取,请参阅DataSourceTransactionManagement。也许这就是原因。

因此,问题是通过Connection获得并在测试代码中使用的dataSource.getConnection()与正在测试的代码中使用的Spring管理的连接不同。因此,在这些连接中执行的查询属于不同的事务,并且在单个线程的许多事务中执行查询通常会导致死锁。

使用DataSourceUtils时,您将获得与正在测试的代码相同的Spring管理连接,以便您在一次交易中执行所有查询。

答案 1 :(得分:0)

我发现您的测试有几个问题:

  1. 您的测试一次测试很多东西。您需要了解的是隔离(也称为模拟)框架,以便您的测试更精细。
  2. 执行数据库测试很棘手(我几乎没有测试过这个层的经验)。您可能最好抽象一点,以便在执行测试时实际上不使用实际资源。如果您发现使用真实资源是必须的,那么它们应该非常简单并且要有一个干净的数据库来防止数据污染您的测试结果。
  3. 不要在测试中重复字符串,就像生产代码(即“Chris”)一样。您可能需要在别处做出参考。根据测试框架的不同,您可能会拥有一个包含共享对象等的基类,您可以根据自己的内容进行自定义。