2015-10-02 47 views
0

我目前正在使用以下方法和单元测试的方法。我认为测试可以/应该被分解成更多的测试,但我不确定有多少测试要写出来,或者更重要的部分是什么,尤其是考虑到该方法使用sql查询等来建立Connection。 。所有帮助表示赞赏。JAVA最佳实践单元测试与JUnit

Java方法:

public static ArrayList<HashMap<String, String>> executeSelect(
     Connection conn, Statement stmt, Query query) { 

    ResultSet rs = null; 
    ArrayList<HashMap<String, String>> serviceRequests = new ArrayList<HashMap<String, String>>(); 

    try { 
     long queryStart = System.nanoTime(); 
     rs = stmt.executeQuery(query.getQuery()); 
     long queryEnd = System.nanoTime(); 
     long queryDuration = queryEnd-queryStart; 
     queryTime = String.valueOf(queryDuration); 

     while (rs.next()) { 

      HashMap<String, String> serviceRequestData = new HashMap<>(); 

      if (QueryUtil.hasColumn(rs, "ID")) { 
       String id = rs.getString("ID"); 
       serviceRequestData.put("ID", id); 
      } 
      else{ 
       serviceRequestData.put("ID", " "); 
      } 
      if (QueryUtil.hasColumn(rs, "FN_Contact")) { 
       String firstName = rs.getString("FN_Contact"); 
       serviceRequestData.put("FN_Contact", firstName); 
      } 
      else{ 
       serviceRequestData.put("FN_Contact", " "); 
      } 
      if (QueryUtil.hasColumn(rs, "LN_Contact")) { 
       String lastName = rs.getString("LN_Contact"); 
       serviceRequestData.put("LN_Contact", lastName); 
      } 
      else{ 
       serviceRequestData.put("LN_Contact", " "); 
      } 
      if (QueryUtil.hasColumn(rs, "Notes")) { 
       String notes = rs.getString("Notes"); 
       serviceRequestData.put("Notes", notes); 
      } 
      else{ 
       serviceRequestData.put("Notes", " "); 
      } 
      if (QueryUtil.hasColumn(rs, "Email")) { 
       String email = rs.getString("Email"); 
       serviceRequestData.put("Email", email); 
      } 
      else{ 
       serviceRequestData.put("Email", " "); 

      } 

      serviceRequests.add(serviceRequestData); 

     } 
    } catch (SQLException e) { 
     e.printStackTrace(); 
     sqlException = true; 
    } 
    return serviceRequests; 
} 

JUnit测试:

@Test 
public void testFirstName() { 
    ArrayList<HashMap<String, String>> testMap = new ArrayList<HashMap<String,String>>(); 
    Connection conn = null; 
    Statement stmt = null; 
    try { 
     Class.forName("com.mysql.jdbc.Driver"); 

     String connectionUrl = "jdbc:mysql://localhost:3306/gc_image"; 
     String connectionUser = "root"; 
     String connectionPassword = "GCImage"; 
     conn = DriverManager.getConnection(connectionUrl, connectionUser, 
       connectionPassword); 
     conn. 
     stmt = conn.createStatement(); 
     Query testQuery = new Query(); 
     testQuery 
       .setQuery("select * from service_request where FN_contact = 'Trevor'"); 
     testMap = QueryController.executeSelect(conn, stmt, testQuery); 

     assertEquals("Janke", testMap.get(0).get("LN_Contact")); 
     assertEquals("Hello World", testMap.get(0).get("Notes")); 
     assertEquals("[email protected]", testMap.get(0).get("Email")); 
     assertEquals("ID", testMap.get(0).get("7")); 

    } catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } catch (SQLException e) { 
     e.printStackTrace(); 
    } finally { 
     try { 
      stmt.close(); 
      conn.close(); 
     } catch (SQLException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

回答

3

你应该确定一下这个测试的设置的一部分是不是测试方法的真正部分开始;即什么是可以提取到@Before@After方法的样板文件。这可能需要您将几个局部变量拉入类变量中。这使每个@Test方法都不那么冗长,并允许您专注于测试下的功能。

接下来您应该删除所有catch块,或者如果您的代码或测试代码引发了意外异常,则应通过类似fail(exception.getMessage())的测试来通过测试。如果删除它们,很容易将单元测试的方法签名更改为throws Exception,以便在引发异常时使其一般性失败。现在,您可能完全无法连接到您的设置中的数据库,并且Junit测试仍将变为绿色!

理想情况下,您可以进行单元测试,涵盖每个if...else块。那会给你10个测试。其中,重点(Assert)将确认serviceRequests中找到/未找到的值的存在。你也应该测试你的异常处理,所以你需要一个强制SQLException被捕获的测试。

我最终会建议,为了减少开销,您可以考虑使用模拟框架,比如Mockito来完全清除数据库I/O。这个SO question有一些嘲笑数据库的好例子。

executeSelect()注意到几个简单的事情,可能是固定的:

  • 取出Connection参数 - 这是不使用的,因为Statement已经被实例化。
  • 考虑抛出SQLException而不是返回一个List - 它给出了一个错误的广告,即DB读取已成功执行。
  • 优先考虑具体类上的接口和抽象类:将您的方法签名和serviceRequestsArrayList<HashMap<String, String>>更改为List<Map<String, String>>。在许多资源中,Josh Bloch的Effective Java是这个主题的很好的参考。

您可以简化这种方法甚至更多 - 这会使得测试更加简单 - 通过下面的代码移动到另一个方法:

long queryStart = System.nanoTime(); 
    rs = stmt.executeQuery(query.getQuery()); 
    long queryEnd = System.nanoTime(); 
    long queryDuration = queryEnd-queryStart; 
    queryTime = String.valueOf(queryDuration); 

,然后只ResultSet突入executeSelect() ...或任何它将被重命名为:)。

+0

非常丰富和全面的答案。谢谢 – GregH

+0

不客气。我想“TL; DR”总是努力将尽可能少的代码放入测试方法中。越少,意图就越清晰,并且更好地相信所要测试的是实际上正在测试的东西:) – Keith

+0

你是什么意思做'失败(e.getMessage))'? 'fail()'不在JUnit库中?这个函数应该包含什么? – GregH