レコードデータの一部のフィールドだけテストする

レコードデータ型の一部のフィールドだけテストする方法について、以前から少し悩んでいた。

data Person
  = Person
    { id :: Int
    , name :: Text
    , age :: Int
    }
    deriving (Eq, Ord, Show)

registerPerson :: Text -> Int -> IO Person

これをテストするために、例えば

let expected = Person 0 "taro" 18
got <- registerPerson "taro" 18
got `shouldBe` expected

などとやりたいところだが、registerPersonは内部でidをランダム生成するため、idに対してexpectationを書くことはできない。

かといって、nameageの各フィールドについてassertionを書き下すのも面倒だ。どうしたものか。

・・・よくよく考えてみればgotからexpectedを作ればいいのか。つまり

let expect p = p { name = "taro", age = 18 }
got <- registerPerson "taro" 18
got `shouldBe` expect got

こうすれば、実質的にexpectで上書きしたフィールドでのみテストが実施されることになる。

また、expectはPerson -> PersonなのでMonoidとして扱うこともできる。組み合わせやすそうな気がする。 ただ、何かの間違いでexpect = idにしてしまうとテストがザルになってしまい、コンパイラも見逃してしまうので注意。

もちろん、このやり方はテスト対象のレコード型が丸ごとEqかつShowじゃないと成立しない。そうでない場合はやはりめんどくさそうだ。