2011-10-04 91 views
1

我在Spring MVC应用程序中遇到了一些单元测试问题。在完全披露的情况下,鉴于我从头开始编写测试套件的经验不足,我错误地设计了我的单元测试。PreparedStatement.execute()在Spring单元测试中挂起

我现在设计的方式是,例如,测试用户服务,测试套件使用原始SQL语句来验证数据是否正确插入/检索/更新。我遇到的问题是,在执行第一个预处理语句之后,后续语句挂在​​方法上。测试的结果呢?“锁等待超时超标;尝试重新启动交易”

基于什么我在线阅读,这很可能是一个事务的管理问题,并有人不释放锁,但我我不知道如何做到最好,甚至在哪里做。

一些相关的代码如下,让我知道是否有更多的代码是必要的。

@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"); 



    } 

回答

3

为了使用原始JDBC Connection s的Spring的事务管理,你需要将它们作为DataSourceUtils.getConnection(dataSource),看到DataSourceTransactionManagement。也许这是原因。

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

当使用DataSourceUtils时,您将获得与正在测试的代码相同的Spring管理连接,以便您的所有查询都在单个事务中执行。

+0

啊很有意思,我会检查一下。其中一件事我从未在文档中看到过...... –

+0

哇,就是这样。我真的希望这是他们在文档中做得更清楚的事情...... –

0

我看到几个问题你的测试:

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