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

Introduce assorted Reactor StepVerifier Refaster rules #1132

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

werli
Copy link
Member

@werli werli commented Apr 11, 2024

Summary

This PR introduces a bunch of rules associated to StepVerifier.Asserations and a trivial StepVerifier rule.


Example

I spotted the following interesting pattern:

Mono.empty()
    .as(StepVerifier::create)
    .expectError(SpecificException.class)
    .verifyThenAssertThat()
    .hasOperatorErrorWithMessage("msg");

Which made me find the interesting StepVerifier#Assertions API. It's quite low-level, and IMO not as straightforward to read and write.

The example itself is most often represented in our code-base using AssertJ's richer API:

Mono.empty()
    .as(StepVerifier::create)
    .verifyErrorSatisfies(t ->
        assertThat(t)
        .isInstanceOf(SpecificException.class)
        .hasMessage("msg"));

Suggested commit message:

Introduce assorted Reactor `StepVerifier` Refaster rules (#1132)

Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Comment on lines 1948 to 1950
/**
* Prefer {@link StepVerifier.LastStep#verifyErrorSatisfies(Consumer)} with AssertJ over more
* contrived alternatives.
*/
static final class StepVerifierLastStepVerifyErrorSatisfiesAssertJ {
@BeforeTemplate
void before(StepVerifier.LastStep step, Class<? extends Throwable> clazz, String message) {
Refaster.anyOf(
step.expectError()
.verifyThenAssertThat()
.hasOperatorErrorOfType(clazz)
.hasOperatorErrorWithMessage(message),
step.expectError(clazz).verifyThenAssertThat().hasOperatorErrorWithMessage(message),
step.expectErrorMessage(message).verifyThenAssertThat().hasOperatorErrorOfType(clazz));
}

@AfterTemplate
@UseImportPolicy(STATIC_IMPORT_ALWAYS)
void after(StepVerifier.LastStep step, Class<? extends Throwable> clazz, String message) {
step.verifyErrorSatisfies(
t -> Assertions.assertThat(t).isInstanceOf(clazz).hasMessage(message));
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Asserting both class and message blocks one to use the recommended StepVerifier.Step#verifyError(Class) or StepVerifier.Step#verifyErrorMessage(String). Choosing an alternative isn't easy since there are many possibilities. Out of all the options, AssertJ's assertions have the richest API, so I'd recommend this.

The StepVerifierVerify rule introduced in this PR aims to rewrite the use-cases in which only one of class or message are asserted by dropping the StepVerifier.Assertions type which allows other rules to rewrite further.

Comment on lines 1916 to 1910
/**
* Prefer {@link StepVerifier.LastStep#verifyErrorMatches(Predicate)} over more verbose
* alternatives.
*/
static final class StepVerifierLastStepVerifyErrorMatchesAssertions {
@BeforeTemplate
void before(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
step.expectError().verifyThenAssertThat().hasOperatorErrorMatching(predicate);
}

@AfterTemplate
void after(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
step.verifyErrorMatches(predicate);
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This case is not exhaustive due to the overload of StepVerifier.LastStep#expectedError, but IMO covers sufficiently.

Copy link
Member

Choose a reason for hiding this comment

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

We can collapse this one into the preceding method.

Comment on lines 1797 to 1798
/** Don't unnecessarily invoke {@link StepVerifier#verifyLater()} multiple times. */
static final class StepVerifierVerifyLater<T> {
@BeforeTemplate
StepVerifier before(StepVerifier stepVerifier) {
return stepVerifier.verifyLater().verifyLater();
}

@AfterTemplate
StepVerifier after(StepVerifier stepVerifier) {
return stepVerifier.verifyLater();
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

The API spec mentions this is a noop. I didn't fully understand why this API may be useful for in the first place.

@werli werli force-pushed the werli/reactor-verify-error-satisfies branch from 7395f93 to 0eea3ac Compare April 11, 2024 21:13
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@Stephan202 Stephan202 added this to the 0.17.0 milestone Apr 12, 2024
Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

Added a commit. Tnx @werli!

Comment on lines 1916 to 1910
/**
* Prefer {@link StepVerifier.LastStep#verifyErrorMatches(Predicate)} over more verbose
* alternatives.
*/
static final class StepVerifierLastStepVerifyErrorMatchesAssertions {
@BeforeTemplate
void before(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
step.expectError().verifyThenAssertThat().hasOperatorErrorMatching(predicate);
}

@AfterTemplate
void after(StepVerifier.LastStep step, Predicate<Throwable> predicate) {
step.verifyErrorMatches(predicate);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

We can collapse this one into the preceding method.

Comment on lines 667 to 682
Mono.empty()
.as(StepVerifier::create)
.expectError()
.verifyThenAssertThat()
.hasOperatorErrorOfType(IllegalStateException.class)
.hasOperatorErrorWithMessage("foo");
Mono.empty()
.as(StepVerifier::create)
.expectError(IllegalStateException.class)
.verifyThenAssertThat()
.hasOperatorErrorWithMessage("bar");
Mono.empty()
.as(StepVerifier::create)
.expectErrorMessage("baz")
.verifyThenAssertThat()
.hasOperatorErrorOfType(IllegalStateException.class);
Copy link
Member

Choose a reason for hiding this comment

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

We can use three different Throwable types like a bit further up.

*/
static final class StepVerifierVerify {
@BeforeTemplate
void before(StepVerifier stepVerifier) {
Copy link
Member

Choose a reason for hiding this comment

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

While in this case arguably incorrect, we do generally match any expression, even if that can lead to compilation errors. (At least this will force the user to clean up the code.)

I'll apply those changes to this PR, though I could also see us change our mind on that in the future.

Copy link
Member Author

Choose a reason for hiding this comment

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

My intent was indeed to avoid breaking code changes by only matching the "dangling" case in which the created StepVerifier.Assertions isn't used or cases we should clearly rewrite.

StepVerifier#verifyThenAssertThat has some valid yet niche usages, see internal usages. Sure, we could suppress the warning here, but IMO the warning would be a false positive. WDYT?

Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Nice improvements!

@rickie rickie force-pushed the werli/reactor-verify-error-satisfies branch from 3742218 to fcbda80 Compare May 2, 2024 06:07
Copy link

github-actions bot commented May 2, 2024

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link

sonarcloud bot commented May 2, 2024

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

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

Successfully merging this pull request may close these issues.

None yet

3 participants