如何测试Swift枚举与相关值的相等性

Jay*_*ske 174 swift

我想测试两个Swift枚举值的相等性.例如:

enum SimpleToken {
    case Name(String)
    case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssert(t1 == t2)
Run Code Online (Sandbox Code Playgroud)

但是,编译器不会编译相等表达式:

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

我是否已经定义了我自己的等于运算符的重载?我希望Swift编译器能够自动处理它,就像Scala和Ocaml一样.

rad*_*dex 226

正如其他人所说,Swift没有自动合成必要的相等运算符.让我提出一个更清洁(恕我直言)的实施,但:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

它远非理想 - 有很多重复 - 但至少你不需要在内部使用if语句进行嵌套开关.

  • 关于这个问题很糟糕的是你需要在switch中使用default语句,所以如果你添加一个新的枚举大小写,编译器不会确保你添加该子句来比较那个新的大小写相等 - 你会只需记住并在以后进行更改时要小心! (36认同)
  • 更好:`case(.Name(let a),.Name(let b)):返回a == b`等. (25认同)
  • 你可以通过用`case(.Name,_)替换`default`来解决@MichaelWaterfall提到的问题:return false; case(.Number,_):return false`. (18认同)

mbp*_*pro 54

实施Equatable是一个矫枉过正的恕我直言.想象一下,你有许多案例和许多不同参数的复杂和大枚举.这些参数也都必须Equatable实现.此外,谁说您在全有或全无的基础上比较枚举案例?如果您正在测试值并且仅存在一个特定的枚举参数,那该怎么办?我强烈建议简单的方法,如:

if case .NotRecognized = error {
    // Success
} else {
    XCTFail("wrong error")
}
Run Code Online (Sandbox Code Playgroud)

......或在参数评估的情况下:

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

在此处查找更详细的说明:https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/

  • 虽然从技术上讲这个答案不能回答问题,但我怀疑这实际上使许多人通过搜索到达此处,意识到他们从一个错误的问题开始。谢谢! (2认同)

pai*_*aiv 14

对于枚举,结构似乎没有编译器生成的相等运算符.

"例如,如果您创建自己的类或结构来表示复杂的数据模型,则该类或结构的"等于"的含义不是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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

[1]请参阅https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43上的 "等效运营商"

  • 为什么@ infix?它工作得很好 (3认同)

Dan*_*ood 14

这是另一种选择.除了通过使用if case语法避免嵌套的switch语句之外,它主要与其他语句相同.我认为这使得它更具可读性(/可忍受)并且具有完全避免默认情况的优点.

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
Run Code Online (Sandbox Code Playgroud)


neo*_*eye 12

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Nik*_*uhe 11

我在单元测试代码中使用这个简单的解决方法:

extension SimpleToken: Equatable {}
func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
    return String(stringInterpolationSegment: lhs) == String(stringInterpolationSegment: rhs)
}
Run Code Online (Sandbox Code Playgroud)

它使用字符串插值来执行比较.我不推荐它用于生产代码,但它简洁并且可以完成单元测试.

  • 我同意,对于单元测试,这是一个不错的解决方案. (2认同)

Dan*_*iel 10

另一种选择是比较案例的字符串表示:

XCTAssert(String(t1) == String(t2))
Run Code Online (Sandbox Code Playgroud)

例如:

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
Run Code Online (Sandbox Code Playgroud)


ter*_*dyl 5

扩展 mbpro 的答案,这是我如何使用该方法检查具有某些边缘情况的关联值的 swift 枚举的相等性。

当然,您可以执行 switch 语句,但有时只检查一行中的一个值也不错。你可以这样做:

// NOTE: there's only 1 equal (`=`) sign! Not the 2 (`==`) that you're used to for the equality operator
// 2nd NOTE: Your variable must come 2nd in the clause

if case .yourEnumCase(associatedValueIfNeeded) = yourEnumVariable {
  // success
}
Run Code Online (Sandbox Code Playgroud)

如果要比较同一个 if 子句中的 2 个条件,则需要使用逗号而不是 &&运算符:

if someOtherCondition, case .yourEnumCase = yourEnumVariable {
  // success
}
Run Code Online (Sandbox Code Playgroud)