0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

XCTestで許容精度付きで浮動小数点数の配列の比較を行う

Posted at

TL;DR

CustomAssertion.swift
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:)にかけていけば問題ない。
よって二つの配列の要素数が一致した場合に比較を行うようにする。

impl1.swift
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。

impl2.swift
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にどのインデックスだったかを記録する

impl3.swift
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)
    }
}

screenshot.png

これでどの要素が一致しないか?がわかるようになった。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?