是否可以在测试用例的主体中对列表进行排序以检查边界数据?

Is it ok to sort a List in the body of the test case to check for boundary data?

我正在测试查询的结果。存储结果的 table 结构如下:

Id  SomeValue  Date        Hour
-----------------------------------
1   foo1       2015-01-01  700
2   foo2       2015-01-01  800
3   foo3       2015-01-01  900
...
18  foo18      2015-01-01  2400
19  bar1       2015-01-02  100
20  bar2       2015-01-02  200
...
41  bar23      2015-01-02  2300
42  bar24      2015-01-02  2400
43  baz1       2015-01-03  100
44  baz2       2015-01-03  200
(and on...)

并且查询接收参数以便根据 DateHour 列进行搜索,如下所示:

SELECT *
FROM table
WHERE
    (date, hour) >= (:dateFrom, :hourFrom)
    AND (date, hour) <= (:dateTo, :hourTo)
-- there's no ORDER BY clause in the query

例如,如果我使用以下值:

查询将 return 所有 Date 的值在 2015-01-012015-01-03 之间的行,hour 的值大于或等于仅 Date = 2015-01-01 小于 700,hour 的值小于或等于 Date = 2015-01-03 的 600。在此示例中,将从数据源中检索所有具有 Date = 2015-01-02 的行。

我在列表中检索执行查询的结果。为了评估结果,我使用了我用来检查列表中的数据是否符合它们的参数值。我正在使用一种方法来检查元素的日期是否在 dateFromdateTo 之间,但我想知道如何测试 hourFrom 和 [=17= 的值].我有以下想法:

哪个选项是正确的?如果 none,最好的策略是什么?。我正在使用 Java 来编写测试,但这个问题更侧重于如何编写测试方法而不是 technology/framework 来使用。此外,我无法修改查询以添加 ORDER BY 子句(这会减轻我的很多工作但不可行)。

我担心最佳实践。我正在考虑对数据进行排序,因此我将对两个元素进行断言,但是我担心我是否还必须测试用于排序的 Comparator 因为它可能会错误地对列表进行排序并且我的测试将失败,而手动检查每个元素意味着对断言使用 if-else 语句,我不确定这是否是一个好习惯。

您提出的两个选项都是正确的。对数据进行排序的选项将更容易实现。

检索结果后对结果进行排序没有错。这是我会采用的方法,但检查每一行也可以。

选择选项 1。遍历数据时,记录最大值和最小值。您只能遍历列表一次。如果你的objective只是为了找到不符合条件的数据的存在,那么你已经找到了可能还没有遍历到最后。这比对列表排序更有效。最坏的情况是 O(n).

我明白您最关心的可能是用尽可能简单的逻辑编写单元测试。这样做会提高您的信心水平,即当单元测试报告成功或失败时,这确实意味着查询返回了好的或坏的结果,而不是您在单元测试的逻辑中编码了错误。拥有绝对最好的性能对你来说甚至都无关紧要。

如果是这种情况,我建议使用非常直接的选项 #1,您只需按顺序检查每个 date/time,并在遇到 [=23= 时立即使单元测试失败] 不在您的 min/max date/time 之内。但是我会通过以下方式调整比较 2 组 date/time 的方法,以保持比较逻辑非常简单:

将每个 date/time 连接并格式化为以下字符串格式:YYYY-MM-DD hhmm(例如:2015-01-01 0700)。换句话说,您的字符串使用所需的零填充进行格式化,因此它的长度始终为 15。遵循这种格式恰恰有一个非常方便的 属性,如果您使用内置的 String.compareTo() 方法比较 2 个这样的字符串,它会准确地比较您的日期。

这样可以让您的逻辑非常简单和可读。这是一个例子(你没有指定,所以我假设你的日期是一个字符串,时间是一个数字。但是你可以根据你的实际类型进行调整):

// starting boundary values
String dateFrom = "2015-01-01";
int hourFrom = 700;
String dateTo = "2015-01-03";
int hourTo = 600;

String formatString = "%s %04d"; // adjust as necessary.

String fromDateTime = String.format(formatString, dateFrom, hourFrom);
String toDateTime = String.format(formatString, dateTo, hourTo);

// execute query here

while (rs.next()) {
    String dateTime = String.format(formatString, rs.getString("date"), rs.getInt("hour"));

    if (fromDateTime.compareTo(dateTime) > 0 || toDateTime.compareTo(dateTime) < 0) {
        throw new Exception("fail unit test");
    }
}

既然是日期,最好使用java日期对象;

import java.io.IOException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class ATest {

    public static void main(String[] args) throws IOException, ParseException {
        String dateFrom = "2015-01-01";
        String dateTo = "2015-01-03";

        // those are actually string as i see.
        String hourFrom = "700";
        String hourTo = "600";


        Date from = str2Date(dateFrom, hourFrom);
        Date to = str2Date(dateTo, hourTo);

        Date any = new GregorianCalendar().getTime();

        // now testing any date using 'before' 'after'
        assert(any.after(from) && any.before(to));

        // or using compareTo
        assert(any.compareTo(from) > 0 && any.compareTo(to) < 0);

        // or simpy comparing epocs, i would use this
        long low = from.getTime();
        long high = from.getTime();

        assert( any.getTime() > low && any.getTime() < high);
    }

    private static Date str2Date(String date_input, String time_input) {

        // probably this is an overkill.
        while (time_input.length() < 4){
            time_input = "0" + time_input;
        }

        String parts[] = date_input.split("-");
        int year = Integer.parseInt(parts[0]); 
        int month = Integer.parseInt(parts[1]); 
        int date = Integer.parseInt(parts[2]);

        int hourOfDay = Integer.parseInt(time_input.subSequence(0, 2).toString()); 
        int minute = Integer.parseInt(time_input.subSequence(2, 4).toString());  
        int second = 0;

        Calendar cal = GregorianCalendar.getInstance();
        cal.set(year, month - 1, date, hourOfDay, minute, second);

        return cal.getTime();
    }
}

排序与否对我来说并不重要(如果列表中的元素数量不是很多)。