Cómo probar la igualdad de enums de Swift con los valores asociados

Quiero probar la igualdad de dos valores Swift enum. Por ejemplo:

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

Sin embargo, el comstackdor no comstackrá la expresión de igualdad:

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

¿Tengo que definir mi propia sobrecarga del operador de igualdad? Esperaba que el comstackdor Swift lo manejara automáticamente, al igual que Scala y Ocaml.

Como otros han notado, Swift no sintetiza los operadores de igualdad necesarios automáticamente. Sin embargo, permítanme proponer una implementación más limpia (en mi humilde opinión)

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

Está lejos de ser ideal, hay mucha repetición, pero al menos no necesitas hacer cambios nesteds con sentencias if dentro.

Implementar Equatable es un exceso en mi humilde opinión. Imagine que tiene enum complicado y grande con muchos casos y muchos parámetros diferentes. Todos estos parámetros tendrán que tener implementado Equatable también. Además, ¿quién dijo que comparas los casos enum sobre la base de todo o nada? ¿Qué tal si está probando el valor y ha anulado solo un parámetro enum particular? Sugeriría un enfoque simple, como:

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

… o en el caso de la evaluación de parámetros:

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

Encuentre una descripción más elaborada aquí: https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/

Parece que no hay comstackdor generado operador de igualdad para enums, ni para structs.

“Si creas tu propia clase o estructura para representar un modelo de datos complejo, por ejemplo, entonces el significado de” igual a “para esa clase o estructura no es algo que Swift pueda adivinar para ti”. [1]

Para implementar la comparación de igualdad, uno escribiría algo como:

 @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] Consulte “Operadores de equivalencia” en https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43.

Aquí hay otra opción. Es principalmente el mismo que los demás, excepto que evita las declaraciones de cambio anidadas mediante el uso de la syntax de if case . Creo que esto lo hace un poco más legible (/ soportable) y tiene la ventaja de evitar por completo el caso predeterminado.

 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 

Estoy usando esta solución simple en el código de prueba de la unidad:

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

Utiliza la interpolación de cadenas para realizar la comparación. No lo recomendaría para el código de producción, pero es conciso y hace el trabajo para las pruebas unitarias.

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

Otra opción sería comparar las representaciones de cadena de los casos:

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

Por ejemplo:

 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 

Otro enfoque que usa if case con comas, que funciona en Swift 3:

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

Así es como escribí en mi proyecto. Pero no recuerdo de dónde saqué la idea. (Busqué en Google ahora pero no vi ese uso.) Cualquier comentario sería apreciado.

t1 y t2 no son números, son instancias de SimpleTokens con valores asociados.

Puedes decir

 var t1 = SimpleToken.Number(123) 

Entonces puedes decir

 t1 = SimpleToken.Name(“Smith”) 

sin un error de comstackdor

Para recuperar el valor de t1, use una instrucción switch:

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

la ‘ventaja’ cuando se compara con la respuesta aceptada es que no hay un caso ‘predeterminado’ en la instrucción de conmutación ‘principal’, por lo que si extiende su enumeración con otros casos, el comstackdor lo forzará a actualizar el rest del código.

 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 

Puedes comparar usando el interruptor

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