Skip to content

Introduce new RenderTester API #15

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

Merged
merged 10 commits into from
Jun 29, 2020
Merged

Introduce new RenderTester API #15

merged 10 commits into from
Jun 29, 2020

Conversation

bencochran
Copy link
Collaborator

@bencochran bencochran commented Jun 28, 2020

Per #16 and square/workflow#702, introduce a new RenderTester API shaped like the following:

NameLoadingWorkflow()
    .renderTester(initialState: .init(state: .loading, token: "user-token"))
    .expect(
        worker: LoadingWorker(token: "user-token"),
        producingOutput: .success("Ben Cochran")
    )
    .expectSideEffect(key: "some-extra-side-effect-just-for-fun")
    .render { rendering in 
        XCTAssertEqual(rendering.title, "Loading")
    }
    .verify(
        action: MyWorkflow.Action.loadSuccess(name: "Ben Cochran")
    )
    .verify(
        output: .complete
    )

The expect methods come in the following shapes:

  • Workflows:
    • expectWorkflow(workflowType: WorkflowType.Type, key: String, producingRendering: WorkflowType.Rendering, producingOutput: WorkflowType.Output?, assertions: ((WorkflowType) -> Void)?)
  • Workers
    • expect(worker: WorkerType, producingOutput: WorkerType.Output?)
  • Side effects
    • expectSideEffect(key: AnyHashable, producingAction: ActionType?)

The verify methods come in the following:

  • Actions
    • verifyAction(assertions: (Action) -> Void)
    • verify(action: Action) (when Equatable)
    • verifyNoAction()
  • Output
    • verifyOutput(assertions: (Output) -> Void)
    • verify(output: Output) (when Equatable)
    • verifyNoOutput()
  • Resulting state
    • verifyState(assertions: (State) -> Void)
    • verify(state: State) (when Equatable)

This also deprecates, but keeps around, the old API (you’ll see some type erasure and contortions in this PR to reimplement the old API on top of the new one).

Checklist

  • Unit Tests
  • UI Tests
  • Snapshot Tests (iOS only)
  • I have made corresponding changes to the documentation

@CLAassistant
Copy link

CLAassistant commented Jun 28, 2020

CLA assistant check
All committers have signed the CLA.

@zach-klippenstein
Copy link
Collaborator

I like the equatable overloads for state and output, filed for Kotlin as square/workflow-kotlin#63

@bencochran bencochran marked this pull request as ready for review June 28, 2020 21:37
@bencochran bencochran force-pushed the bc/new-render-tester branch from 65c6a2e to 45ad7fa Compare June 28, 2020 22:45
@dhavalshreyas
Copy link
Contributor

This is really cool! I was worried about having to overload the same RenderExpectations for the different types of Workers. This will help a lot.

let rendering: ExpectedWorkflowType.Rendering
let output: ExpectedWorkflowType.Output?

init(key: String, rendering: ExpectedWorkflowType.Rendering, output: ExpectedWorkflowType.Output?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will need something like in: https://github.com/square/workflow-swift/pull/11/files#diff-d274c3ab6b1b700864fdd0b96d30b731R129

to support custom matching for Workers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we? We don’t actually want to reproduce/change the matching behavior since the RenderContext won’t have additional matching logic for Workers. I’d pictured something like this:

extension RenderTester {
    public func expect<ExpectedWorkerType: Worker>(
        worker expectedWorker: ExpectedWorkerType,
        key: String = "",
        producingOutput output: ExpectedWorkerType.Output? = nil
    ) -> RenderTester<WorkflowType> {
        return self.expectWorkflow(
            type: WorkerWorkflow<ExpectedWorkerType>,
            key: key,
            producingRendering: (),
            producingOutput: output,
            assertions: { actualWorker in
                XCTAssert(expectedWorker.isEquivalent(to: actualWorker))
            }
        )
    }
}

(though I do think I should update assertions to give the file and line so we can correctly place the error where it should be)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, that's right, fantastic!

A slight change, we'll get the Workflow here.

extension RenderTester {
    public func expect<ExpectedWorkerType: Worker>(
        worker expectedWorker: ExpectedWorkerType,
        key: String = "",
        producingOutput output: ExpectedWorkerType.Output? = nil
    ) -> RenderTester<WorkflowType> {
        return self.expectWorkflow(
            type: WorkerWorkflow<ExpectedWorkerType>,
            key: key,
            producingRendering: (),
            producingOutput: output,
            assertions: { actualWorkflow in
                XCTAssert(actualWorkflow.worker.isEquivalent(to: expectedWorker))
            }
        )
    }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, yep!

@bencochran
Copy link
Collaborator Author

After noting “though I do think I should update assertions to give the file and line so we can correctly place the error where it should be” I realized it would be nicer if expectations asserted on the line of the expectation rather than on render, so I updated to do that (which also means assertions on expectWorkflow doesn’t need file and line ✨).

I also fixed the fact that WorkflowRenderTesterFailureTests wouldn’t compile on Xcode 11 and filed #18.

import Workflow
import XCTest

struct AppliedAction<WorkflowType: Workflow> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be wrapped in an #if DEBUG?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think so if the comment about @testable import being the issue was correct (since this file does a standard import)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants