-
Notifications
You must be signed in to change notification settings - Fork 47
[fix]: improve optional handling in TestApplyContext #361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,12 @@ import XCTest | |
| @testable import Workflow | ||
|
|
||
| extension WorkflowAction { | ||
| /// Returns a state tester containing `self`. | ||
| /// Returns a `WorkflowActionTester` with the given state before the `WorkflowAction` has been applied to it. | ||
| /// | ||
| /// - Parameters: | ||
| /// - state: The `WorkflowType.State` instance that specifies the state before the `WorkflowAction` has been applied. | ||
| /// - workflow: An optional `WorkflowType` instance to be used if the `WorkflowAction` needs to read workflow properties off of the `ApplyContext` parameter during action application. If this parameter is unspecified, attempts to access the `WorkflowType`'s properties will error in the testing runtime. | ||
| /// - Returns: An appropriately-configured `WorkflowActionTester`. | ||
| public static func tester( | ||
| withState state: WorkflowType.State, | ||
| workflow: WorkflowType? = nil | ||
|
|
@@ -70,6 +75,19 @@ extension WorkflowAction { | |
| /// .assert(output: .finished) | ||
| /// .assert(state: .differentState) | ||
| /// ``` | ||
| /// | ||
| /// If the `Action` under test uses the runtime's `ApplyContext` to read values from the | ||
| /// current `Workflow` instance, then an instance of the `Workflow` with the expected | ||
| /// properties that will be read during `send(action:)` must be supplied like: | ||
| /// ``` | ||
| /// MyWorkflow.Action | ||
| /// .tester( | ||
| /// withState: .firstState, | ||
| /// workflow: MyWorkflow(prop: 42) | ||
| /// ) | ||
| /// .send(action: .exampleActionThatReadsWorkflowProp) | ||
| /// .assert(...) | ||
| /// ``` | ||
| public struct WorkflowActionTester<WorkflowType, Action: WorkflowAction> where Action.WorkflowType == WorkflowType { | ||
| /// The current state | ||
| let state: WorkflowType.State | ||
|
|
@@ -209,10 +227,25 @@ struct TestApplyContext<Wrapped: Workflow>: ApplyContextType { | |
| case .workflow(let workflow): | ||
| return workflow[keyPath: keyPath] | ||
| case .expectations(var expectedValues): | ||
| guard let value = expectedValues.removeValue(forKey: keyPath) as? Value else { | ||
| fatalError("Attempted to read value \(keyPath as AnyKeyPath), when applying an action, but no value was present. Pass an instance of the Workflow to the ActionTester to enable this functionality.") | ||
| guard | ||
| // We have an expected value | ||
| let value = expectedValues.removeValue(forKey: keyPath), | ||
| // And it's the right type | ||
| let value = value as? Value | ||
|
||
| else { | ||
| // We're expecting a value of optional type. Error, but don't crash | ||
| // since we can just return nil. | ||
| if Value.self is OptionalProtocol.Type { | ||
| reportIssue("Attempted to read value \(keyPath as AnyKeyPath), when applying an action, but no value was present. Pass an instance of the Workflow to the ActionTester to enable this functionality.") | ||
|
||
| return Any?.none as! Value | ||
| } else { | ||
| fatalError("Attempted to read value \(keyPath as AnyKeyPath), when applying an action, but no value was present. Pass an instance of the Workflow to the ActionTester to enable this functionality.") | ||
| } | ||
| } | ||
| return value | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private protocol OptionalProtocol {} | ||
| extension Optional: OptionalProtocol {} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without separating the dictionary lookup from the cast, if you didn't pass in a
Workflowinstance to the.tester()API and theapply()implementation was reading a value of optional type, this line would always 'pass' and returnnilwhen we really want it to fail with the informative error messages below.