DbUnitのTIMESTAMP型のカラムに正確な日時を渡す
DbUnitで(ファイルではなく)コード上でアサーションの期待値を設定したいとき、ITableのインスタンスを作りますが、その際Datatype.TIMESTAMP型のカラムにはjava.sql.Timestamp型の値を自分で作って入れるのが一番正確でよさそうという話です。
コード例はKotlinです。(Javaや他のJVM言語を使用している場合は適宜読み替えてください)
課題
RDBのTimestamp型のカラムをアサートしたい。
以下のようなイメージ。この例では、timestamp
がそれにあたる。
import org.dbunit.Assertion
import org.dbunit.database.IDatabaseConnection
import org.dbunit.dataset.Column
import org.dbunit.dataset.DefaultTable
import org.dbunit.dataset.datatype.DataType
fun assertionExample() {
val expected = DefaultTable("test_table", arrayOf(
Column("value", DataType.VARCHAR),
Column("timestamp", DataType.TIMESTAMP)
))
expected.addRow(arrayOf(
"test_value",
"2020-11-04T19:00:54.663674+09:00"
))
val connection: IDatabaseConnection = createConnection() // connectionの生成は省略
val actual = connection.createDataSet().getTable("test_table")
Assertion.assertEquals(expected, actual)
}
しかし、TIMESTAMP型のカラムに入れられる値はなかなか限られていて、たとえばこの例(ISO8601形式の文字列を入れる場合)は以下のエラーで失敗する。
org.dbunit.dataset.datatype.TypeCastException: Unable to typecast value <2020-11-04T19:00:54.663674+09:00> of type <java.lang.String> to TIMESTAMP
では、どのような値なら入れられるのか?
解決
利用側でjava.sql.Timestamp
型を作れば、正確な日時を渡すことができる。
今回の例では、以下のようにして渡すことになる。
// importは省略
fun assertionExample() {
val offsetDateTime = OffsetDateTime.parse("2020-11-04T19:00:54.663674+09:00")
val instant = offsetDateTime.toInstant()
val expectedTimestamp = java.sql.Timestamp.from(instant)
val expected = DefaultTable("test_table", arrayOf(
Column("value", DataType.VARCHAR),
Column("timestamp", DataType.TIMESTAMP)
))
expected.addRow(arrayOf(
"test_value",
expectedTimestamp
))
// アサーションは省略
}
説明
TimestampDataType
型のコードを読むと、typeCast
というメソッドでさまざまな値からjava.sql.Timestamp
型への変換に対応していることがわかる。
ここで変換した値が、最終的なアサーションに使われる。
しかし、おおむねレガシーなjava.util.Date
を中心とした構成になっていて、Java8で導入されたNew Date/Time APIにまつわる型や文字列フォーマットには対応していない。
また、Date型の精度はミリ秒であり、ナノ秒単位の値を保持することができない。そのため、どのような渡し方をした場合でも、Date型を経由した時点で精度がミリ秒に落ちてしまい、ナノ秒単位で値が保持されているTimestampとのアサーションが失敗する事態に陥る。
結果として、最も正確な値でアサーションするには、New Date/Time APIの型だけを経由して、利用側でjava.sql.Timestamp
型を作って渡すのがよいということになる。
Discussion