test
Swift

UnitTestで自作構造体を比較したい

みなさんtestしてますか?

わたしはもちろんできてません:expressionless:
しようとは思っているのですが状態です。
そこで、とりあえず始めてみたらtestを書く習慣がつくのではないかと思い始めました。

そこで、詰まったのが自作構造体の比較です

XCTestには、XCTAssertEqualというメソッドあります。
このメソッドは、Equatableに準拠しているものを比べて、同じならテスト成功。違うとテスト失敗にしてくれます。

文字列の場合

MyTests.swift
func testString() {
    let expected = "foo"
    let actual = "foo"
    XCTAssertEqual(expected, actual)
}

成功しましたね:relaxed:
String型はEquatableに準拠しているんですね!

自作構造体の場合

user.swift
struct User {
    let name: String
    let age: Int
}

自作構造体を作りました!
このUserを比較したい時がきっとあるはず!

MyTests.swift
func testUser() {
    let expected = User(name: "foo", age: 10)
    let actual = User(name: "foo", age: 10)
    XCTAssertEqual(expected, actual)
}

比較したいのならば、Equatableに準拠しなさいと言われます。
なので、Equatableに準拠させます

user.swift
extension User: Equatable {
    static func ==(lhs: User, rhs: User) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

そうすると、testUser()メソッドがテストでき、成功になりました!(やったね)

まとめ

自作構造体でもEquatableに準拠させることで比較することができま...したが、自作構造体をプロジェクトで使うときもEquatableが使えてしまいます。
そこで、テストの時だけ、自作構造体をEquatableに準拠させたいと思います。

自作構造体の比較(テストの時だけ)

user.swift
struct User {
    let name: String
    let age: Int
}

まずは、自作構造体を作ります
そして、テスト書きます

MyTests.swift
func testUser() {
    let expected = User(name: "foo", age: 10)
    let actual = User(name: "foo", age: 10)
    XCTAssertEqual(expected, actual)
}

そして、自作構造体をEquatableに準拠させるのですが、user.swiftではなくMyTests.swiftに書きます

MyTests.swift
class MyTests: XCTestCase {
    func testUser() {
        let expected = User(name: "foo", age: 10)
        let actual = User(name: "foo", age: 10)
        XCTAssertEqual(expected, actual)
    }   
}

extension User: Equatable {
    static func ==(lhs: User, rhs: User) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

このようになり、XCTAssertEqualで自作構造体が使えるようになりました。
ここにEquatableの準拠を書くことで、プロジェクト側は自作構造体がEquatableに準拠してない状態で使えます
試しにプロジェクト側でEquatableを使ってみます。

ViewController.swift
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let user = User(name: "foo", age: 10)
        let user2 = User(name: "foo", age: 10)

        if user == user2 { //Binary operator '==' cannot be applied to two 'User' operands
            print("ok")
        }
    }
}

自作構造体の比較をしているところで、Binary operator '==' cannot be applied to two 'User' operandsというエラーがでます。
つまり、テストでは使えるのに、プロジェクトでは使えないという目的を果たしました:clap:

真・まとめ

テストだけで、比較がしたかったり、なにかしたい場合があった時に、テストの方にextensionで拡張をすると、プロジェクトの方には反映されないことがわかりました。
これでみなさんのテストが捗ることを祈っております。