Skip to content

Conversation

@dwilding
Copy link
Contributor

@dwilding dwilding commented Sep 28, 2025

This PR adds a how-to guide for migrating unit tests away from Harness. I've not linked the new guide from any other docs yet, or removed How to write legacy unit tests for a charm. I'll leave those tasks for a follow-up PR.

Preview doc

event.add_status(ops.MaintenanceStatus("waiting for container"))
event.add_status(ops.ActiveStatus())

... # Handle other events.
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 was originally going to include an action that executes a command in the workload container. I removed that because I wasn't confident in the approach, and this PR was getting big enough already. I might add that back in a follow-up PR

@dwilding dwilding marked this pull request as ready for review September 28, 2025 23:39
Copy link
Contributor

@dimaqq dimaqq left a comment

Choose a reason for hiding this comment

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

Only a partial review.

I feel that as the examples get longer and longer, at some point the doc becomes tedious.

Could the examples be trimmed to testing one thing at a time, with the mocking step, but without the actual mocks?

A tutorial would be self-contained, while the how-to guide could be more focused.

WDYT?

Rule of thumb: the doc renders 8 pages long for me (reasonable font, large monitor). At this point it could be split into 4~6 separate docs.

Comment on lines 160 to 165
def _on_get_value_action(self, event: ops.ActionEvent) -> None:
"""Handle the get-value action."""
if event.params["value"] == "please fail":
event.fail("Action failed, as requested")
else:
event.set_results({"out-value": event.params["value"]})
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd drop this entirely, or if it's really needed, then:

Suggested change
def _on_get_value_action(self, event: ops.ActionEvent) -> None:
"""Handle the get-value action."""
if event.params["value"] == "please fail":
event.fail("Action failed, as requested")
else:
event.set_results({"out-value": event.params["value"]})
def _on_get_value_action(self, event: ops.ActionEvent) -> None:
# See above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this makes sense, thanks. I've replaced the whole method by:

...  # _on_get_value_action is unchanged.

Copy link
Collaborator

@tonyandrewmeyer tonyandrewmeyer left a comment

Choose a reason for hiding this comment

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

A few small suggestions, but overall I think this works well. I think this is as long as we would want to start with - if we get a lot of common "how to convert X" questions in the future then we could add more, but I don't think it's worth trying to be exhaustive.

It's a shame that we can't do a see-more to the PE guide, but it seems like that's internal only. There's some overlap, but also some additional content (although the custom events bit is a little out of date now). Their approach of using tabs to have the harness/scenario code uses less space, but I think your approach here tells the story more clearly without having to flick back and forth.

It does feel a little heavy on collect-status, but as that's a significant difference, perhaps that's ok. I'm happy with it as-is.

For more information about state-transition testing, see:

- [](#write-unit-tests-for-a-charm)
- [The reference docs](/reference/ops-testing), especially [](ops.testing.State) and [](ops.testing.CharmEvents)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting to choose CharmEvents here. I would have thought that Context and State were the two you'd most what to see. I guess CharmEvents has all of the events and they aren't 100% the same as in ops, although very similar.

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 reworked this bit, partly after your feedback. To make the links easier to find (and to make the intro shorter) I've added a dedicated See more section at the end of the doc. In that, I've linked to Context, State, and CharmEvents.

Copy link
Contributor

@james-garner-canonical james-garner-canonical left a comment

Choose a reason for hiding this comment

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

This is looking great to me, modulo the existing suggestions.

@dwilding
Copy link
Contributor Author

Thanks all for the feedback! I've addressed everything, but won't merge until next week (when Tony is back).

While applying the feedback, I made a couple of changes to reduce the cognitive load of things that aren't relevant to the core point. Thanks Dima for your comments around this.

  • Removed the definition of the MockWorkload class, which was duplicated between the Harness and state-transition tests. Instead, I've left a comment:

    Prepare a mock workload object with matching config, assuming we've defined a MockWorkload class with suitable attributes and methods.

  • Reduced charm code to the charm class only, since that's all we really care about.

Copy link
Collaborator

@tonyandrewmeyer tonyandrewmeyer left a comment

Choose a reason for hiding this comment

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

Re-approving, in case you were after that (the recent changes all look good to me).

@dwilding dwilding merged commit 3ade1fc into canonical:main Oct 13, 2025
52 checks passed
@dwilding dwilding deleted the harness-migration branch October 13, 2025 23:14
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.

4 participants