[swift] 관련 값으로 Swift 열거 형의 동등성을 테스트하는 방법

두 개의 Swift 열거 형 값의 동등성을 테스트하고 싶습니다. 예를 들면 다음과 같습니다.

enum SimpleToken {
    case Name(String)
    case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssert(t1 == t2)

그러나 컴파일러는 등식을 컴파일하지 않습니다.

error: could not find an overload for '==' that accepts the supplied arguments
    XCTAssert(t1 == t2)
    ^~~~~~~~~~~~~~~~~~~

동등 연산자의 자체 과부하를 정의해야합니까? Swift 컴파일러가 Scala 및 Ocaml처럼 자동으로 처리하기를 바랐습니다.



답변

스위프트 4.1 이상

마찬가지로 @jedwidz가 유용하게 스위프트 4.1 (인해, 지적 SE-0185 , 스위프트 또한 합성 지원 EquatableHashable연관된 값에 대한 열거.

따라서 Swift 4.1 이상을 사용하는 경우 다음은 XCTAssert(t1 == t2)작동 하는 필요한 방법을 자동으로 합성 합니다. 열쇠는 Equatable열거 형에 프로토콜 을 추가하는 것 입니다.

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)

스위프트 4.1 이전

다른 사람들이 지적했듯이 Swift는 필요한 항등 연산자를 자동으로 합성하지 않습니다. 그러나 더 깨끗한 (IMHO) 구현을 제안하겠습니다.

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}

public func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
    switch (lhs, rhs) {
    case let (.Name(a),   .Name(b)),
         let (.Number(a), .Number(b)):
      return a == b
    default:
      return false
    }
}

이상적인 것은 아니지만 반복이 많이 있지만 적어도 if 문으로 중첩 스위치를 수행 할 필요는 없습니다.


답변

구현 Equatable은 과도한 IMHO입니다. 많은 경우와 많은 다른 매개 변수가있는 복잡하고 큰 열거 형이 있다고 상상해보십시오. 이 매개 변수들도 모두 Equatable구현 해야합니다 . 또한 누가 열거 형 사례를 전혀 또는 전혀 비교하지 않았다고 누가 말했습니까? 값을 테스트하고 특정 열거 형 매개 변수를 하나만 스텁 한 경우는 어떻습니까? 나는 다음과 같은 간단한 접근법을 강력히 제안한다.

if case .NotRecognized = error {
    // Success
} else {
    XCTFail("wrong error")
}

… 또는 파라미터 평가의 경우 :

if case .Unauthorized401(_, let response, _) = networkError {
    XCTAssertEqual(response.statusCode, 401)
} else {
    XCTFail("Unauthorized401 was expected")
}

자세한 설명은 https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/에서 확인 하십시오.


답변

열거 형이나 구조체에 대해 컴파일러에서 항등 연산자를 생성하지 않은 것 같습니다.

“예를 들어 복잡한 데이터 모델을 표현하기 위해 고유 한 클래스 또는 구조를 생성하는 경우 해당 클래스 또는 구조에 대한”동일 “의 의미는 Swift가 사용자에게 추측 할 수있는 것이 아닙니다.” [1]

평등 비교를 구현하려면 다음과 같이 작성하십시오.

@infix func ==(a:SimpleToken, b:SimpleToken) -> Bool {
    switch(a) {

    case let .Name(sa):
        switch(b) {
        case let .Name(sb): return sa == sb
        default: return false
        }

    case let .Number(na):
        switch(b) {
        case let .Number(nb): return na == nb
        default: return false
        }
    }
}

[1] https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43의 “등가 연산자”를 참조하십시오.


답변

다른 옵션이 있습니다. 구문을 사용하여 중첩 된 switch 문을 피한다는 점을 제외하면 주로 다른 것과 동일 if case합니다. 나는 이것이 약간 더 읽기 쉽고 (/ 견딜 수 있음) 기본 사례를 피하는 이점을 가지고 있다고 생각합니다.

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}
extension SimpleToken {
    func isEqual(st: SimpleToken)->Bool {
        switch self {
        case .Name(let v1):
            if case .Name(let v2) = st where v1 == v2 { return true }
        case .Number(let i1):
            if case .Number(let i2) = st where i1 == i2 { return true }
        }
        return false
    }
}

func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
    return lhs.isEqual(rhs)
}

let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")

t1 == t1  // true
t1 == t2  // false
t3 == t3  // true
t3 == t4  // false
t1 == t3  // false


답변

enum MyEnum {
    case None
    case Simple(text: String)
    case Advanced(x: Int, y: Int)
}

func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch (lhs, rhs) {
    case (.None, .None):
        return true
    case let (.Simple(v0), .Simple(v1)):
        return v0 == v1
    case let (.Advanced(x0, y0), .Advanced(x1, y1)):
        return x0 == x1 && y0 == y1
    default:
        return false
    }
}


답변

단위 테스트 코드 에서이 간단한 해결 방법을 사용하고 있습니다.

extension SimpleToken: Equatable {}
func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
    return String(stringInterpolationSegment: lhs) == String(stringInterpolationSegment: rhs)
}

문자열 보간을 사용하여 비교를 수행합니다. 프로덕션 코드에는 권장하지 않지만 간결하고 단위 테스트 작업을 수행합니다.


답변

다른 옵션은 케이스의 문자열 표현을 비교하는 것입니다.

XCTAssert(String(t1) == String(t2))

예를 들면 다음과 같습니다.

let t1 = SimpleToken.Number(123) // the string representation is "Number(123)"
let t2 = SimpleToken.Number(123)
let t3 = SimpleToken.Name("bob") // the string representation is "Name(\"bob\")"

String(t1) == String(t2) //true
String(t1) == String(t3) //false