Sourceryを用いてテスト対象となるカスタムタイプをSwiftCheck::Arbitraryに自動的に準拠させる

SwiftCheck

従来のXCTestを用いたテスト方法

従来のXCTestを用いたテスト方法では入力値と期待値を明確に示す.この方法をExample based testと呼ぶ.この方法では,整数値の足し算のテストを書くときは,バグが起きそうな例外値を含めていくつか入力値とその入力値に対する期待値を示す.しかし,入力値と期待値のセットをいくつ用意するばいいのか,またはどのような入力値を用意するかの判断は難しい.加えて,この判断を誤るとテストの品質が落ちる.

SwiftCheckを用いたテスト方法

そこで,SwiftCheckを用いたテスト方法では具体的な入力値については考えない.この方法をProperty based testと呼ぶ.この方法では,テスト対象となる関数の性質に着目し,その性質を示す.その性質が満たされるかをSwiftCheckが用意する大量の入力値を用いてテストする.

Foundation frameworkのmax関数をSwiftCheckを用いてテストすると下記のようになる.
コードの中に具体的な入力値や期待値が含まれていないことがわかる.
func testMaxWithInt() {
        property("xがy以上であった場合にはxを返す.また,yがx以上であった場合にはxを返す.") <- forAll { (x: Int, y: Int) in
            return x >= y ? max(x, y) == x : max(x, y) == y
        }

        property("xがy以上でありyがx以上であった場合にはx==y(antisymmetric law)") <- forAll { (x: Int, y: Int) in
            return max(x, y) == x && max(y, x) == y ? x == y : x != y
        }
    }

関数の性質の見つけ方

関数の性質を見つけるには下記の3つに着目すれば良いとiOSアプリ開発 自動テストの教科書に述べられていた.
1.対称性のあるアルゴリズム
2.ランダム性のあるアルゴリズム
3.高速で複雑なアルゴリズム vx 低速で明白なアルゴリズム

カスタムタイプを対象としたテスト

カスタムタイプComplexNumber(複素数)の大きさをmax関数で比較し,その結果が正しいのかをテストする.
struct ComplexNumber {
    let realPart: Double
    let imaginarryPart: Double

    var size: Double {
        return sqrt(pow(realPart, 2) + pow(imaginarryPart, 2))
    }
}

extension ComplexNumber: CustomStringConvertible {
    var description: String {
        let operatorString = imaginarryPart >= 0 ? "+" : "-"
        return "\(realPart)\(operatorString)j\(abs(imaginarryPart))"
    }
}

extension ComplexNumber: Comparable {
   // 複素数自体は大小の比較ができないから,このインタフェースは良くない.
    static func < (lhs: ComplexNumber, rhs: ComplexNumber) -> Bool {
        return lhs.size < rhs.size
    }
}