【问题标题】:JAVA best practice unit testing with JUnit使用 JUnit 进行 JAVA 最佳实践单元测试
【发布时间】:2015-10-02 02:37:09
【问题描述】:

我目前正在使用以下方法和该方法的单元测试。我认为测试可以/应该分解为更多测试,但我不确定要为此编写多少测试,或者更重要的部分特别考虑到该方法涉及建立Connection,使用sql查询等...感谢所有帮助。

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("janke@gmail.com", 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();
        }
    }

}

【问题讨论】:

    标签: java unit-testing junit


    【解决方案1】:

    您应该首先确定该测试设置的哪些部分不是测试方法的真正部分;即,可以提取到@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 ——它会给出一个错误的广告,表明数据库读取已成功执行。
    • 优先选择接口和抽象类而不是具体类:将方法签名和serviceRequestsArrayList&lt;HashMap&lt;String, String&gt;&gt; 更改为List&lt;Map&lt;String, String&gt;&gt;。在众多资源中,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() ...或者将其重命名为:)。

    【讨论】:

    • 非常翔实和全面的答案。谢谢
    • 不客气。我想“TL;DR”是始终努力将尽可能少的代码放入测试方法中。越少,意图就越清晰,并且对想要测试的东西实际上就是正在测试的东西更有信心:)
    • fail(e.getMessage)) 是什么意思? fail() 不在 JUnit 库中?这个函数应该包含什么?
    • 关于您的回答的另一个问题:如果我要将您发布的代码分开到它自己的方法中,那么它将执行两次而不是应该的一次查询(如果我删除了 ..stmt.executeQuery..部分然后它不会真正测试任何东西)有没有办法解决这个问题还是我不​​理解对吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    相关资源
    最近更新 更多