Skip to content

Conversation

@hadley
Copy link
Member

@hadley hadley commented Jul 30, 2025

Fixes #2113. Fixes #2132. Fixes #2072.

@hadley hadley requested a review from EmilHvitfeldt July 31, 2025 17:20
@hadley
Copy link
Member Author

hadley commented Jul 31, 2025

Could you review this one too please @EmilHvitfeldt?

Copy link
Member

@EmilHvitfeldt EmilHvitfeldt left a comment

Choose a reason for hiding this comment

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

very nice, definitely better than before! the advanced examples bring a lot to the table

### Capture value and label
The first step in any expectation is to use `quasi_label()` to capture a "labelled value", i.e. an list that contains both the value (`$val`) for testing and a label (`$lab`) for messaging. This is a pattern that exists for fairly esoteric reasons; you don't need to understand, just copy and paste it 🙂.

Next you need to check each way that `object` could be broken. In most cases, it's easier to check for problems one by one, using early returns to `fail()` when any expectation is violated as that makes it easier to write failure messages. It's good practice to state both what the object is and what you expected in your failures.
Copy link
Member

Choose a reason for hiding this comment

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

Next you need to check each way that object could be broken.

I think "broken" sends the wrong message. Maybe something like

Next you need to check each way that the object could violate the stated expectation you are testing for.

Copy link
Member Author

Choose a reason for hiding this comment

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

I liked violated expectation as a term. Thanks!

act <- quasi_label(rlang::enquo(object))
if (!is.atomic(act$val) || !is.list(act$val)) {
if (!is.atomic(act$val) && !is.list(act$val)) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (!is.atomic(act$val) && !is.list(act$val)) {
if (!(is.atomic(act$val) || is.list(act$val))) {

weakly held opinion. since it isn't computational crucial, i think this is more readable

Copy link
Member Author

Choose a reason for hiding this comment

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

I find that hard to read too because of all the parens. But for this case I can just use rlang::is_vector() instead. (And that's generally what I'd recommend - if you don't like the and-not form, write an informatively named helper function or intermediate variable.)

Comment on lines +169 to +175
act_n <- length(act$val)
if (act_n != n) {
msg <- sprintf("%s has length %i, not length %i.", act$lab, act_n, n)
return(fail(msg, trace_env = trace_env))
}
expect_snapshot_failure(expect_length(x, 11))
})
pass(act$val)
Copy link
Member

Choose a reason for hiding this comment

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

maybe a note should be added that both fail() and pass() should be used in the helper, as it will be used in return() explicitly or otherwise.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point; tried to clarify below

@hadley hadley merged commit a012eef into main Aug 1, 2025
13 checks passed
@hadley hadley deleted the custom-expectations branch August 1, 2025 13:54
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.

Compound expectations Make it easier to correctly write expectations "Custom expectations" vignette should discuss "trace_env"

3 participants