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

时间:2014-06-21 08:31:45

标签: 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那样。

12 个答案:

答案 0 :(得分:214)

正如其他人所说,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
    }
}

它远非理想 - 有很多重复 - 但至少你不需要在里面用if语句做嵌套开关。

答案 1 :(得分:45)

实施Equatable是一种矫枉过正的恕我直言。想象一下,你有许多案例和许多不同参数的复杂和大枚举。这些参数也必须实现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/

答案 2 :(得分:14)

似乎没有编译器为枚举生成相等运算符,也没有结构。

  

“例如,如果您创建自己的类或结构来表示复杂的数据模型,则该类或结构的”等于“的含义不是Swift可以为您猜测的。”[1] < / p>

为了实现相等比较,可以编写如下内容:

@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]参见&#34;等效运算符&#34;在https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43

答案 3 :(得分:12)

这是另一种选择。除了通过使用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

答案 4 :(得分:11)

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
    }
}

答案 5 :(得分:9)

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

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

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

答案 6 :(得分:9)

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

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

答案 7 :(得分:2)

t1和t2不是数字,它们是具有相关值的SimpleTokens实例。

你可以说

var t1 = SimpleToken.Number(123)

然后你可以说

t1 = SimpleToken.Name(“Smith”) 

没有编译器错误。

要从t1检索值,请使用switch语句:

switch t1 {
    case let .Number(numValue):
        println("Number: \(numValue)")
    case let .Name(strValue):
        println("Name: \(strValue)")
}

答案 8 :(得分:2)

与接受的答案相比,'优点'是'main'开关语句中没有'default'的情况,所以如果你用其他情况扩展你的枚举,编译器将强制你更新其余的代码

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}
extension SimpleToken {
    func isEqual(st: SimpleToken)->Bool {
        switch self {
        case .Name(let v1):
            switch st {
            case .Name(let v2): return v1 == v2
            default: return false
            }
        case .Number(let i1):
            switch st {
            case .Number(let i2): return i1 == i2
            default: 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

答案 9 :(得分:2)

从Swift 4.1开始,只需将Equatable协议添加到您的枚举并使用XCTAssertXCTAssertEqual

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

答案 10 :(得分:0)

扩展mbpro的答案,这就是我使用该方法检查快速枚举与关联值和某些边缘情况是否相等的方式。

当然,您可以执行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
}

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

if someOtherCondition, case .yourEnumCase = yourEnumVariable {
  // success
}

答案 11 :(得分:-1)

您可以使用开关

进行比较
enum SimpleToken {
    case Name(String)
    case Number(Int)
}

let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)

switch(t1) {

case let .Number(a):
    switch(t2) {
        case let . Number(b):
            if a == b
            {
                println("Equal")
        }
        default:
            println("Not equal")
    }
default:
    println("No Match")
}