# Testing enum cases with associated values

**URL:** <https://forums.swift.org/t/testing-enum-cases-with-associated-values/7091>\
**Category:** Discussion\
**Created:** [November 16, 2017, 9:56pm UTC](https://forums.swift.org/t/testing-enum-cases-with-associated-values/7091 "2017-11-16T21:56:16Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![lvsti](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lvsti/32/2080_2.png) [@lvsti](https://forums.swift.org/u/lvsti)\
**Post date:** [November 16, 2017, 9:56pm UTC](https://forums.swift.org/t/testing-enum-cases-with-associated-values/7091/1 "2017-11-16T21:56:16Z")

</div>

I know I'm a bit late to the party but I have just found this thread and  
decided to put my 2 cents, in the hope that it will resurrect the  
discussion and eventually something gets done.

In the thread (quoted below), everybody expressed their desire to be able  
to easily test enum cases of enums with (and without) associated values,  
one way or another. What I haven't seen among the suggestions is the  
liberation of pattern matching.

Currently we have 3 constructs that support pattern matching: if, guard,  
and switch. The problem with all of them is that they are control  
statements, so anytime we need a boolean expression (whether X matches a  
pattern or not) we need to wrap the statement and somehow get the boolean  
result out of it. This seems unnecessary; pattern matching could as well  
exist on its own as an expression that evaluates to a boolean, e.g.:

enum Result\<T\> {  
&nbsp;&nbsp;&nbsp;&nbsp;case success(T)  
&nbsp;&nbsp;&nbsp;&nbsp;case failure  
}

let r = Result.success(42)  
let rs: [Result\<Int\>] = [.success(2), .success(3), .failure, .success(5),  
.failure]

let simpleTest: Bool = (case .success = r)  
let simpleFiltered: [Result\<Int\>] = rs.filter { case .success = $0 }

Now, we could take this even further and allow matching for subparts:

let subpartTest: Bool = (case .success(let v) = r, v \< 100)  
// --\> true  
let subpartFiltered: [Result\<Int\>] = rs.filter { case .success(let v) = $0,  
v % 2 == 1 }  
// --\> [.success(3), .success(5)]

or perhaps with a more verbose syntax:

let subpartTest2: Bool = (case .success(let v) = r where v \< 100)  
let subpartFiltered2: [Result\<Int\>] = rs.filter { case .success(let v) = $0  
where v % 2 == 1 }

I'm not familiar with the parsing challenges of the above suggestion but  
these expressions don't look wildly different from what we already see in  
currently supported pattern matching constructs.

So, any concerns about this? Am I overlooking something?

Tamas

> I see your point about conformances. In my example,

AuthenticationResponse isn't a generic type, so the conformances spec won't  
apply.

> I'll go out on a limb and say that 80%+ of the use cases for equality

will be to distinguish enum cases, not the associated values.

> I do like your proposal though. In the thread talking about it someone

mentioned a 'derived' keyword to specify conformance and derivation  
simultaneously. I like the idea of being able to say:

> &nbsp;&nbsp;&nbsp;&nbsp;enum AuthenticationResponse: derived Equatable { ... }
> 
> Still, even with derived conformance, it can still be very useful to be

able to test against specific enum cases when the associated values aren't  
Equatable. Maybe this example would make it clearer:

> ```swift
> enum OpaqueResponse {
> case success(OpaqueResult)
> case failure
> }
> 
> ```
> 
> If OpaqueResult is from a library module and its implementation uses

private variables, it may not be easy to make it conform to Equatable. Yet,  
it would be nice to be able to say:

> ```swift
> let result: OpaqueResponse = request()
> if request == .failure { ... }
> 
> ```
> 
> A more realistic example comes from the world of Rx, where the original

issue I have came from:

> ```swift
> let result: Observable<OpaqueResponse> = request()
> 
> result.filter { $0 == .failure } ...
> result.filter { $0 == .success } ...
> 
> // The best we can do today looks more like:
> result.filter { if case .failure = $0 { return true} else { return false
> 
> ```

} }

> result.filter { $0.isFailure } // but we have to define isFailure

ourselves

> // There's a similar issue with assertions  
> if case .failure = result { assert(false) }  
> assert({ if case .failure = result { return true } else { return false }

}())

> ```swift
> 
> I agree it&#39;s less of an issue with test cases\. The issue arises when we
> 
> ```

want a Bool valued expression...

> Andy
> 
> > Conditional conformances doesn't solve enum equality though, because

there likely won't be a way to utter "enum Foo : Equatable where \<all types  
across all associated value payloads are Equatable\>" with the generics  
syntax that's available, and conformance alone wouldn't be able to derive  
the implementation. It'll require some work on the compiler's part to  
generate the right implementation—I mentioned a draft proposal I wrote a  
while back for auto-equality of enums and structs where all the components  
are equatable \<[allevato’s gists · GitHub](https://gist.github.com/allevato/)  
2fd10290bfa84accfbe977d8ac07daad\> in another thread, but as Robert  
mentioned, the missing piece is how users opt in/out of it.

> > If you just want to check the case of an enum in a test, what about this?
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;enum Foo {  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case bar  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case baz(Int)  
> > &nbsp;&nbsp;&nbsp;&nbsp;}  
> > &nbsp;&nbsp;&nbsp;&nbsp;let foo = Foo.baz(5)  
> > &nbsp;&nbsp;&nbsp;&nbsp;guard case .baz = foo else { XCTFail("expected baz"); return }
> > 
> > The "return" being required isn't ideal because XCTFail doesn't return

Never, but other than that it's not \*horrible\*. You can do whatever pattern  
matching you need to use or ignore associated values as part of your  
comparison.

> > > Yes, here's a reference to the conditional conformance proposal which

was accepted:

> > > [GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.](https://github.com/apple/swift-evolution/blob/master/)

proposals/0143-conditional-conformances.md

> > > But as I mention in my post, there are cases it doesn't handle.

Specifically, when the associated types for an enum aren't equatable  
themselves, it's still possible to define equality on the enum cases  
without associated values.

> > > Andy
> > > 
> > > > Automatic “equatability” of aggregates that contain equatable members

has been discussed on this list quite a few times. (I think I had a branch  
at one point that was exploring this kind of deriving mechanism… It seems  
to be lost to the sands of time). Everybody seems to agree that it’s a  
worthwhile feature, but there needs to be thought put into how it is  
exposed to the end user. e.g. Should we continue with silently  
implementing these protocols if we can, or should there be some kind of  
annotation to tell the compiler to only synthesize what the user wants?

> > > > > Enums with associated values can be very useful in Swift, but once

you add associated values you lose some properties, especially equality:

> > > > > ```swift
> > > > > enum AuthenticationResponse {
> > > > > case success
> > > > > case alert(Alert)
> > > > > case reauthenticate
> > > > > }
> > > > > 
> > > > > ```
> > > > > 
> > > > > Testing for a specific case requires a switch statement or the if

pattern match syntax:

> > > > > &nbsp;&nbsp;&nbsp;&nbsp;if case .success = response { … }
> > > > > 
> > > > > But while this works well for control flow, it doesn’t work well for

cases where we want a Bool, such as assert(). There are also common  
situations with lists and libraries like RxSwift where a filtering function  
uses a Bool valued closure. In these situations the best we can do is write  
functions like:

> > > > > ```swift
> > > > > enum AuthenticationResponse {
> > > > > case success
> > > > > case alert(Alert)
> > > > > case reauthenticate
> > > > > 
> > > > > var isSuccess: Bool {
> > > > > if case .success = self {
> > > > > return true
> > > > > } else {
> > > > > return false
> > > > > }
> > > > > }
> > > > > 
> > > > > var isReauthenticate: Bool {
> > > > > if case .reauthenticate = self {
> > > > > return true
> > > > > } else {
> > > > > return false
> > > > > }
> > > > > }
> > > > > 
> > > > > var isAlert: Bool {
> > > > > if case .alert(_) = self {
> > > > > return true
> > > > > } else {
> > > > > return false
> > > > > }
> > > > > }
> > > > > }
> > > > > 
> > > > > ```
> > > > > 
> > > > > Any suggestions better than writing out each of these functions

explicitly?

> > > > > The conditional conformances proposal coming in Swift 4 solves some

of this issue, but not completely. If Alert isn’t Equatable, it is still  
useful to test whether the result is .success. For example:

> > > > > &nbsp;&nbsp;&nbsp;&nbsp;assert(response == .success)
> > > > > 
> > > > > This is perfectly intelligible and I would argue that equality should

be defined for enums with associated values omitted:

> > > > > &nbsp;&nbsp;&nbsp;&nbsp;assert(response == .alert)
> > > > > 
> > > > > Here we are ignoring the associated values, and merely checking if

the enum case is the same.

> **···**
>
> > Le 18 janv. 2017 à 05:51, Andy Chou via swift-evolution \<swift-evolution at swift.org\> a écrit :
> > 
> > > On Jan 17, 2017, at 6:38 PM, Tony Allevato \<tony.allevato at gmail.com\> wrote:
> > > 
> > > > On Tue, Jan 17, 2017 at 5:59 PM Andy Chou via swift-evolution \<swift-evolution at swift.org\> wrote:
> > > > 
> > > > > On Jan 17, 2017, at 5:45 PM, Robert Widmann \<devteam.codafi at gmail.com\> wrote:
> > > > > 
> > > > > > On Jan 17, 2017, at 7:15 PM, Andy Chou via swift-evolution \<swift-evolution at swift.org\> wrote:
> > > > > > 
> > > > > > Andy
> > > > > > 
> > > > > > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > > > > > swift-evolution mailing list  
> > > > > > swift-evolution at swift.org  
> > > > > > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)
> > > > 
> > > > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > > > swift-evolution mailing list  
> > > > swift-evolution at swift.org  
> > > > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)
> > 
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution at swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)
