境界の仕様

ソフトウェアテストの#2のアドベントカレンダーの12/16のエントリになります。

今回は自分がやらかしてしまった境界の仕様の話をしようと思います。

あるセンサーのデータを収集する機能があります。
そのままデータ収集を続けるとデータが多くなって、DBの容量がいっぱいになりますので、沢山たまったデータを削除するパージという機能が必要です。
それではそのパージの機能ですが、どちらの仕様(コード)が良いでしょうか?
※ここでは仕様に沿ってコードが実装されると仮定しています。

ID 仕様 条件の疑似コード例
A0 ある日付より前のデータは消す date < "2018/12/11"
B0 ある日付以前のデータは消す date <= "2018/12/10"

もちろんどちらでも大丈夫そうに見えますね。 dateは日付なのでどちらも期待通りに動作します。

さて、ここに時間の概念が入ってきたらどうなるでしょうか?

ID 仕様 条件の疑似コード例
A1 ある日時より前のデータは消す dateTime < "2018/12/11 00:00:00"
B1 ある日時以前のデータは消す dateTime <= "2018/12/10 00:00:00"

あれ?B1のパターンは2018/12/10 12:00:00のデータが消えないというバグがありそうですね。 これを期待通りに動作するように変更すると以下のようになります。

ID 仕様 条件の疑似コード例
A2 ある日時より前のデータは消す dateTime < "2018/12/11 00:00:00"
B2 ある日時以前のデータは消す dateTime <= "2018/12/10 23:59:59"

23:59:59なんて何か嫌な感じですね。

さて、さらにミリ秒の概念が入ってきたらどうでしょうか?

ID 仕様 条件の疑似コード例
A3 ある日時より前のデータは消す dateTime < "2018/12/11 00:00:00.000"
B3 ある日時以前のデータは消す dateTime <= "2018/12/10 23:59:59.999"

ここまで来るとだんだんわかってきますね。
日付というのは実は範囲を持っていて、2018/12/10 00:00:00.000 から 2018/12/10 23:59:59.999…(省略)になります。
仕様作成時、コード実装次に、以下の図の●と○の中間をちょっと考えることで変更に強い仕様やコードになると思います。
f:id:nemorine:20181216074832p:plain

ちなみに自分が埋め込んだバグは 以前の仕様に則って、<=を使用し、

dateTime <= "2018/12/10 23:59:59.999"

と実装したのですが、小数点以下がなんと6桁のケースもありました ><
すなわち、2018/12/10 23:59:59.999001 ~ 2018/12/10 23:59:59.999999が削除されないことになります。
レビュー指摘されて、事なきを得ましたが危なかったですねぇ。