2011-10-04 114 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」)一樣。您可能需要在別處做出參考。根據測試框架的不同,您可能會被允許擁有一個包含共享對象等的基類,您可以根據自己的心中的內容進行自定義。