将Postgres的current_timestamp设置为JUnit测试的常量?

时间:2016-04-25 13:48:48

标签: java postgresql junit

我正在使用JUnit来测试访问Postgres数据库的存储库。

我的所有测试数据都是手动设置的,所以我确切地知道我希望在结果中得到什么值。我在测试使用current_timestamp的语句时遇到了麻烦,例如select * from phone_number where current_timestamp <= expires;

问题是我的测试数据是常量,但current_timestamp在每次测试执行时都会提前。

有没有办法让current_timestamp返回一个常量值?

当然这将是几个选项之一,我还有三个解决方案:

  • 在我的测试数据中定义基准日期(即一个纪元,类似base_date timestamp default current_time)。所有其他数据被定义为该时期数据之后的间隔(例如,为base_date + interval '1 day')。知道我的测试的粗略运行时,我可以设置expires时间戳,这些时间戳在我的测试期间肯定不会超过(例如,在base_date之后的几小时,当运行时为秒时)。在我的测试数据中包含变量值会使检查变得更加困难。
  • 我可以停止使用current_timestamp并在我的所有语句中使用额外的时间戳参数,正常使用(在Java中)类似于System.currentTimeMillis()new java.util.Date()(在任何情况下, '现在'),在我的测试中,我可以传递不变的日期。我不是这个解决方案的粉丝,因为a)它引入了正常使用中不需要的更改(测试之外)b)其他参数是其他错误的来源。
  • 定义一个返回current_timestamp的存储过程,除非存在某个值,表示“测试模式”已打开且应返回常量。这实际上可能效果最好,但如果在Postgres中有一种简单的方法可以控制current_timestamp返回的内容,则可能没有必要。

1 个答案:

答案 0 :(得分:1)

您可以在单元测试中包装数据库调用,而不是尝试在数据库本身中执行某些操作,或者只是为了允许进行特定测试而重构代码,而是使用类型化的文字替换查询字符串中的current_timestamp值。例如:

public ResultSet executeQuery(String sql) {
    return wrapped.executeQuery(
        sql.replaceAll("\\bcurrent_timestamp\\b",
                       "'20160425 10:12'::timestamptz"));
}

直接实现ConnectionStatement包装器可能会让人感到痛苦,因为这些接口定义了很多方法。如果您已经使用其他东西包装了JDBC访问,或者如果您正在使用可以创建部分模拟的模拟库(如Mockito&#39; spy()),则可以更容易。如果您不做其中任何一项,使用java.reflect.Proxy可能会有所帮助。