首页 > 解决方案 > DateFormat UnitTest 在 Jenkins 中失败,但在本地失败

问题描述

我对服务进行了单元测试,当我执行 Jenkins 作业时,每个测试方法都正确通过,除了一个。
但是这种测试方法适用于我的机器,无论是使用 Eclipse 还是使用mvn命令。

    // TARGET_RUN_DATE_OF_YEAR = "2018-01-01"
    @Test
    public void dateToTimestamp() {
        Service service = getService();

        String df = "YYYY-MM-dd";
        String invalid = "INVALID";

        // Check success
        Timestamp timestamp = service.dateToTimestamp(TARGET_RUN_DATE_OF_YEAR, df);
        Assert.assertEquals(service.getTodayTimestamp(), timestamp); // <-- Fail here
        // Check failure
        Assert.assertNull(service.dateToTimestamp(TARGET_RUN_DATE_OF_YEAR, invalid));
        Assert.assertNull(service.dateToTimestamp(invalid, df));
    }

该服务有多种方法:

服务中的dateToTimestamp方法:

    private Timestamp dateToTimestamp(String date, DateFormat df) throws ParseException {
        return new Timestamp(df.parse(date).getTime());
    }

    @Override
    public Timestamp dateToTimestamp(String date, String dateFormatString) {
        try {
            DateFormat df = new SimpleDateFormat(dateFormatString);
            return dateToTimestamp(date, df);
        } catch (Exception e) {
            log.warn("Exception during conversion of date to timestamp, exception : {}", e);
            return null;
        }
    }

正如我之前所说,测试在我的电脑上完美运行,但在 Jenkins 上却不行(向@Ignore这个方法添加注释,使工作成功)。
启动作业时,我收到此错误:

失败的测试:dateToTimestamp(com.test.service.ServiceImplTest):预期:<2018-01-01 00:00:00.0> 但为:<2017-12-31 00:00:00.0>

我可以保证的是,即使在 Jenkins 中,该dateToTimestamp方法也采用 parameters TARGET_RUN_DATE_OF_YEAR,即"2018-01-01"dateFormat String as "YYYY-MM-dd"。但仍以2017-12-31 00:00:00.0时间戳的形式返回。

有任何想法吗?

标签: javaunit-testingjenkinsdate-format

解决方案


java.time

我假设你想要一个Timestamp用于你的 SQL 数据库。不要Timestamp在 2019 年使用它。该课程设计不良且早已过时。

  • 如果 SQL 端的数据类型是timestamp with time zone(它应该是时间戳),请OffsetDateTime在 Java 中使用。
  • 如果在 SQL 方面您只需要一个timestamp(没有时区),请LocalDateTime在 Java 中使用。

代码示例:

    String dateString = "2018-01-01";
    OffsetDateTime odt = LocalDate.parse(dateString)
            .atStartOfDay()
            .atOffset(ZoneOffset.UTC);

    System.out.println(odt);

输出是:

2018-01-01T00:00Z

现在您可以OffsetDateTime使用以下方式将您的 JDBC 传递给 JDBC:

    yourPreparedStatement.setObject(4, odt);

你的代码出了什么问题?

我相信您遇到了两个问题的组合:

  • YYYY在格式模式字符串中使用大写字母是不正确的。大写字母Y表示基于周的年份,仅对周数有用。对于旧的和麻烦的SimpleDateFormat,你需要小写y一年。
  • (JVM 上)你的机器和 Jenkins 服务器有不同的默认语言环境。

展示:

    String dateString = "2018-01-01";
    String dateFormatString = "YYYY-MM-dd"; // Incorrect format pattern string
    DateFormat df = new SimpleDateFormat(dateFormatString);
    System.out.println(df.parseObject(dateString));

我的电脑上的输出(丹麦语言环境,欧洲/哥本哈根时区):

2018 年 1 月 1 日星期一 00:00:00 CET

但是,如果我首先这样做:

    Locale.setDefault(Locale.US);

— 那么上面代码段的输出是不同的,即:

2017 年 12 月 31 日星期日 00:00:00 CET

发生的情况是,SimpleDateFormat放弃从基于周的年、月和月中的某天确定确切日期,而只是为您提供基于周的年的第一个日期。典型的行为是SimpleDateFormat给你一个不可能正确的结果,却假装一切都好。在某些语言环境中,一周从星期一开始,您会得到 Mon Jan 01,这恰好 与您的字符串一致。在其他地区(例如美国),一周从星期日开始,因此您会得到上一年的 12 月 31 日星期日。

关联


推荐阅读