Skip to content
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

feat: add testing utilities #178

Merged
merged 14 commits into from
Feb 20, 2025
Merged

feat: add testing utilities #178

merged 14 commits into from
Feb 20, 2025

Conversation

cschmatzler
Copy link
Contributor

@cschmatzler cschmatzler commented Feb 3, 2025

Adds testing utilities to Noora in debug builds that record output and allow us to do snapshot testing.
The output follows the format of

stderr: ▌ ✖ Error 
stderr: ▌ Error 
stderr: ▌
stderr: ▌ Sorry this didn’t work. Here’s what to try next: 
stderr: ▌  ▸ Consider creating an issue using the following link: https://github.com/tuist/tuist/issues/new/choose

Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
@cschmatzler cschmatzler changed the title Add testing utilities feat: Add testing utilities Feb 3, 2025
Signed-off-by: Christoph Schmatzler <[email protected]>
#if DEBUG
import Rainbow

public class MockNoora: Noora, CustomStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

@fortmarek we ended up going for subclassing here because we only wanted to extend the parent with some convenient API for testing, and dependency-inject a pipeline that would record the output. Let us know what you think of the architecture and if you'd do things differently.

Copy link
Member

Choose a reason for hiding this comment

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

Instead of subclassing, I'd lean to using a protocol and then using the real implementation under-the-hood:

public struct NooraMock: Noorable {
    private let noora: Noorable
    public init(theme: Theme = .default, terminal: Terminaling = Terminal()) {
        self.noora = Noora(theme: theme, terminal: terminal)
    }

       func yesOrNoChoicePrompt(
        title: TerminalText?,
        question: TerminalText
    ) -> Bool {
      noora.yesOrNoChoicePrompt(...)
   }
}

It does mean the mock needs to define all Noorable methods but I think that's an ok trade-off.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've applied this, I think. Would appreciate some comments on implementation details - will add documentation once the actual structure is signed off by you guys.

Copy link
Member

Choose a reason for hiding this comment

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

Aligned 👍

Copy link
Member

@fortmarek fortmarek left a comment

Choose a reason for hiding this comment

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

Overall, very much onboard with this 🙂

Since the NooraMock is something consumers of this library will use, I would add:

  • some documentation for how to use the mock to the docs
  • some tests that use NooraMock to snapshot test the output.

#if DEBUG
import Rainbow

public class MockNoora: Noora, CustomStringConvertible {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of subclassing, I'd lean to using a protocol and then using the real implementation under-the-hood:

public struct NooraMock: Noorable {
    private let noora: Noorable
    public init(theme: Theme = .default, terminal: Terminaling = Terminal()) {
        self.noora = Noora(theme: theme, terminal: terminal)
    }

       func yesOrNoChoicePrompt(
        title: TerminalText?,
        question: TerminalText
    ) -> Bool {
      noora.yesOrNoChoicePrompt(...)
   }
}

It does mean the mock needs to define all Noorable methods but I think that's an ok trade-off.

#if DEBUG
import Rainbow

public class MockNoora: Noora, CustomStringConvertible {
Copy link
Member

Choose a reason for hiding this comment

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

Given this is something we expect consumers of Noora to use to snapshot test the output, I'd add documentation along some usage guidance.

Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
Comment on lines 26 to 47
public class StandardPipelineEventsRecorder {
var events: [StandardOutputEvent] = []
}

public struct StandardOutputEvent: Equatable {
let type: StandardPipelineType
let content: String
}

public enum StandardPipelineType: CustomStringConvertible {
public var description: String {
switch self {
case .error: "stderr"
case .output: "stdout"
}
}

case output
case error
}

public struct StandardPipeline: StandardPipelining {
Copy link
Member

Choose a reason for hiding this comment

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

Do all of these types actually need to be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

CustomStringConvertible
{
private let noora: Noorable
var standardPipelineEventsRecorder = StandardPipelineEventsRecorder()
Copy link
Member

Choose a reason for hiding this comment

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

Is this what developer should be using for snapshot testing or rather the description? Having an example using this mock in action would help a bit with the review as it's not super obvious how this is supposed to be used.

Either way, this property should either be private or public, but I don't see why it would be internal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tuist/tuist@main...cs/noora-success-messages

Here's a branch with what Pedro and I imagined in regards to usage.

Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
Copy link
Member

@fortmarek fortmarek left a comment

Choose a reason for hiding this comment

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

@cschmatzler I'm onboard with the current changes. We can merge this once we add some documentation, including an example 🙏

Signed-off-by: Christoph Schmatzler <[email protected]>
Signed-off-by: Christoph Schmatzler <[email protected]>
@cschmatzler cschmatzler changed the title feat: Add testing utilities feat: add testing utilities Feb 19, 2025
Copy link
Member

@fortmarek fortmarek left a comment

Choose a reason for hiding this comment

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

:shipit:

@pepicrft pepicrft merged commit 8d8153c into main Feb 20, 2025
9 of 10 checks passed
@pepicrft
Copy link
Contributor

Solid work 👏🏼

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.

3 participants