So testen Sie die Gleichheit von Swift-Enums mit zugehörigen Werten

Ich möchte die Gleichheit zweier Swift-Enum-Werte testen. Beispielsweise:

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

Der Compiler kompiliert den Gleichheitsausdruck jedoch nicht:

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

Muss ich meine eigene Überladung des Gleichheitsoperators definieren? Ich hatte gehofft, der Swift-Compiler würde das automatisch erledigen, ähnlich wie Scala und Ocaml.

Solutions Collecting From Web of "So testen Sie die Gleichheit von Swift-Enums mit zugehörigen Werten"

Wie andere bemerkt haben, synthetisiert Swift die notwendigen Gleichheitsoperatoren nicht automatisch. Lassen Sie mich jedoch eine sauberere (IMHO) Umsetzung vorschlagen:

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

Es ist alles andere als ideal – es gibt eine Menge Wiederholungen – aber zumindest müssen Sie keine verschachtelten Schalter mit if-statementen darin einfügen.

Equatable ist ein Overkill IMHO. Stellen Sie sich vor, Sie haben ein kompliziertes und großes Enum mit vielen Fällen und vielen verschiedenen Parametern. Diese Parameter müssen ebenfalls Equatable implementiert haben. Außerdem, wer hat gesagt, Sie vergleichen Enum-Fälle auf Alles-oder-Nichts-Basis? Wie wäre es, wenn Sie Wert testen und nur einen bestimmten Enum-Parameter ausgegeben haben? Ich würde einen einfachen Ansatz vorschlagen, wie:

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

… oder bei der Parameterauswertung:

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

Finden Sie ausführlichere Beschreibung hier: https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/

Es scheint keinen von Compilern erzeugten Gleichheitsoperator für Enums oder für Strukturen zu geben.

“Wenn Sie zum Beispiel Ihre eigene class oder Struktur erstellen, um ein komplexes Datenmodell zu repräsentieren, dann ist die Bedeutung von” gleich “für diese class oder Struktur nicht etwas, das Swift für Sie erraten kann.” [1]

Um den Gleichheitsvergleich zu implementieren, würde man etwas schreiben wie:

 @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] Siehe “Äquivalenzoperatoren” unter https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43

Hier ist eine andere Option. Es ist im Wesentlichen das Gleiche wie die anderen, außer dass die verschachtelten switch-statementen durch die Verwendung der if case Syntax vermieden werden. Ich denke, dies macht es etwas lesbarer (/ erträglicher) und hat den Vorteil, den Standardfall insgesamt zu vermeiden.

 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 

Ich verwende diese einfache Problemumgehung im Komponententestcode:

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

Es verwendet String-Interpolation, um den Vergleich durchzuführen. Ich würde es nicht für den Produktionscode empfehlen, aber es ist prägnant und macht den Job für Komponententests.

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

Eine andere Möglichkeit wäre, die String-Repräsentationen der Fälle zu vergleichen:

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

Beispielsweise:

 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 

Ein anderer Ansatz mit if case mit Kommas, der in Swift 3 funktioniert:

 enum { case kindOne(String) case kindTwo(NSManagedObjectID) case kindThree(Int) static func ==(lhs: MyEnumType, rhs: MyEnumType) -> Bool { if case .kindOne(let l) = lhs, case .kindOne(let r) = rhs { return l == r } if case .kindTwo(let l) = lhs, case .kindTwo(let r) = rhs { return l == r } if case .kindThree(let l) = lhs, case .kindThree(let r) = rhs { return l == r } return false } } 

So schrieb ich in meinem Projekt. Aber ich kann mich nicht erinnern, wo ich die Idee hatte. (Ich habe gerade gegoogelt, aber keine solche Verwendung gesehen.) Jeder Kommentar würde geschätzt werden.

t1 und t2 sind keine Zahlen, sie sind Instanzen von SimpleTokens mit zugeordneten Werten.

Sie können sagen

 var t1 = SimpleToken.Number(123) 

Sie können dann sagen

 t1 = SimpleToken.Name(“Smith”) 

ohne einen Compilererrors.

Um den Wert von t1 abzurufen, verwenden Sie eine switch-statement:

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

Der “Vorteil” im Vergleich zur akzeptierten Antwort ist, dass es in der statement “main” keinen Standardfall gibt. Wenn Sie also Ihre Enum mit anderen Fällen erweitern, wird der Compiler Sie zwingen, den Rest des Codes zu aktualisieren.

 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 

Sie können mit Schalter vergleichen

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