TL;DR
import XCTest
/// 浮動小数点数の配列同士を許容精度付きで比較する
func assertEqual<T: FloatingPoint>(_ lhs: [T], _ rhs: [T], accuracy: T, file: StaticString = #filePath, line: UInt = #line) {
guard lhs.count == rhs.count else {
XCTFail("\(lhs) is not equal to \(rhs)", file: file, line: line)
return
}
for index in lhs.indices {
XCTAssertEqual(lhs[index], rhs[index], accuracy: accuracy, "index: \(index)", file: file, line: line)
}
}
背景
XCTestには様々なアサーションが用意されている。
そのうちの一つにXCTAssertEqual(::accuracy:_:file:line:)がある。
これは、二つの浮動小数点数の差が誤差(accuracy)に納まるか?をチェックするアサートである。
浮動小数点数の演算には誤差がつきものであり計算順序が異なる場合に厳密には一致しなくなるが、その誤差でテストが落ちてしまうことは煩わしいため用意されているアサートである。
しかし、このアサートは二つの浮動小数点数の比較のみ行えるアサートであるため、浮動小数点数の配列同士を比較に使用することは出来ない。
誤差を含む浮動小数点数の配列同士を比較する必要があったため今回カスタムアサーションを作成した。
方針
以下の条件を満たしたアサーションを作る
- 指定した誤差以内であれば許容する
- XCTestのアサーションのようにテストに失敗した行にエラーが表示される
- 一致しない要素がどれかわかりやすくする
指定した誤差以内であれば許容する
基本的には要素一つ一つをXCTAssertEqual(_:_:accuracy:_:file:line:)
にかけていけば問題ない。
よって二つの配列の要素数が一致した場合に比較を行うようにする。
func assertEqual<T: FloatingPoint>(_ lhs: [T], _ rhs: [T], accuracy: T) {
guard lhs.count == rhs.count else {
XCTFail("\(lhs) is not equal to \(rhs)")
return
}
for index in 0..<lhs.count {
XCTAssertEqual(lhs[index], rhs[index], accuracy: accuracy)
}
}
XCTestのアサーションのようにテストに失敗した行にエラーが表示される
XCTestのアサーションが失敗したとき、コンパイルエラーのように失敗した行の右側にエラーが表示される。
カスタムアサーションでも表示されるようにする。
表示するためにはXCTFail()
とXCTAssertEqual(_:_:accuracy:_:file:line:)
に#filePath
と#line
を渡してやればOK。
func assertEqual<T: FloatingPoint>(_ lhs: [T], _ rhs: [T], accuracy: T, file: StaticString = #filePath, line: UInt = #line) {
guard lhs.count == rhs.count else {
XCTFail("\(lhs) is not equal to \(rhs)", file: file, line: line)
return
}
for index in lhs.indices {
XCTAssertEqual(lhs[index], rhs[index], accuracy: accuracy, file: file, line: line)
}
}
これで失敗したとき失敗した行にエラーが表示されるようになる
一致しない要素がどれかわかりやすくする
前記二つで要素が一致しなかった場合エラーが表示されるようになった。
しかし現状だとどの要素が一致していないのかが分かりづらい。そのためmessageにどのインデックスだったかを記録する
func assertEqual<T: FloatingPoint>(_ lhs: [T], _ rhs: [T], accuracy: T, file: StaticString = #filePath, line: UInt = #line) {
guard lhs.count == rhs.count else {
XCTFail("\(lhs) is not equal to \(rhs)", file: file, line: line)
return
}
for index in lhs.indices {
XCTAssertEqual(lhs[index], rhs[index], accuracy: accuracy, "index: \(index)", file: file, line: line)
}
}
これでどの要素が一致しないか?がわかるようになった。