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 AssertJ templates #37

Merged
merged 6 commits into from
Apr 11, 2022

Conversation

rickie
Copy link
Member

@rickie rickie commented Feb 2, 2022

  • Introduce new file AssertJMapTemplates
  • Introduce templates for assertThat(string).{matches,doesNotMatch}(regex);
  • Introduce templates to improve expressions assertThat(number % 2).isEqualTo({0,1});

@rickie rickie changed the title Introduce AssertJMapTemplates Introduce assorted AssertJ templates Feb 2, 2022
Copy link

@EnricSala EnricSala left a comment

Choose a reason for hiding this comment

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

Just a couple of questions but LGTM 👍

Comment on lines +240 to +237
@BeforeTemplate
AbstractLongAssert<?> before(long number) {
return assertThat(number % 2).isEqualTo(1);

Choose a reason for hiding this comment

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

Just checking, do we need to check also the boxed variant of all of these or are they covered?

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 one!

Refaster does autoboxing for us when it is trying to match. You can turn that off by using @NoAutoboxing, see a check for that here. When the type of a variable is going to be inferred, there is a resolution phase where this method is called.

Let's add test cases to make it clear that this is also included! Pushing something in a bit.

Comment on lines 25 to 32
@BeforeTemplate
AbstractMapAssert<?, ?, K, V> before(AbstractMapAssert<?, ?, K, V> mapAssert, K key, V value) {
return mapAssert.containsExactlyInAnyOrderEntriesOf(ImmutableMap.of(key, value));
}

@AfterTemplate
AbstractMapAssert<?, ?, K, V> after(AbstractMapAssert<?, ?, K, V> mapAssert, K key, V value) {
return mapAssert.containsOnly(Map.entry(key, value));

Choose a reason for hiding this comment

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

Not sure I'm a fan of this rule, and special cases for singletons in general 🤔

IMO it leads to non-uniform assertions, for example my OCD is triggered by this:

assertThat(obj.getA()).containsOnly(Map.entry("1", 1));
assertThat(obj.getB()).containsExactlyInAnyOrderEntriesOf(ImmutableMap.of("2", 2, "3", 3));

I think I would prefer using the multi case as the default, so:

assertThat(obj.getA()).containsExactlyInAnyOrderEntriesOf(ImmutableMap.of("1", 1));
assertThat(obj.getB()).containsExactlyInAnyOrderEntriesOf(ImmutableMap.of("2", 2, "3", 3));

Copy link
Member Author

Choose a reason for hiding this comment

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

Proposing something else now, PTAL 😄. @EnricSala @werli

In many other cases (e.g. for Lists and Sets) we do something similar to: containsExactlyInAnyOrderEntriesOf.
When we know there is only one element, we generally make the assertion stronger by ommiting the InAnyOrder part, such that we can also verify that it is in "in order". In the case of the Map we can use the containsExactlyEntriesOf which: Verifies that the actual map contains only the entries of the given map and nothing else, *in order*.

What do you guys think of this?

Choose a reason for hiding this comment

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

In many other cases (e.g. for Lists and Sets) we do something similar to: containsExactlyInAnyOrderEntriesOf.

This is what I'm not sure I agree with 🤔 having special cases for the singletons leads to assertions looking different, and if I need to make a change where a test is now supposed to return more elements then I need to make even more changes. Would be curious to the reasoning to better understand why not just use the multi-element form everywhere :)

When we know there is only one element, we generally make the assertion stronger by ommiting the InAnyOrder part, such that we can also verify that it is in "in order".

Just for my understanding, if we expect to find only one element anyway, why does it matter that we omit the InAnyOrder?

Copy link
Member Author

Choose a reason for hiding this comment

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

You mention good points here.

if I need to make a change where a test is now supposed to return more elements then I need to make even more changes.

Yeah, that is indeed true.

Would be curious to the reasoning to better understand why not just use the multi-element form everywhere :)

To be frank, I consulted @Stephan202 about this and I think it is good to tag him so he can explain it in his words 😛.

Anyway, I'm going to give it a try and explain some parts of his reasoning (makes at least a good practice for me):
To start, the method name with InAnyOrder is unnecessary long for the single element case as it does not add any information about what is being asserted. So in a way, it obfuscates the (intention of the) code.

Feel like how I'm explaining it doesn't make a strong case 😅.

Copy link
Member

@werli werli left a comment

Choose a reason for hiding this comment

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

I couldn't add anything to Enric's nice comments.

Nice to see these getting added. 👍

}

@BeforeTemplate
AbstractIntegerAssert<?> before(short number) {
Copy link
Member Author

@rickie rickie Feb 16, 2022

Choose a reason for hiding this comment

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

Ai, this one needs to be a AbstractShortAssert...

Copy link
Member Author

@rickie rickie Feb 18, 2022

Choose a reason for hiding this comment

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

I was wrong; the isEqualTo(1) returns a AbstractIntegerAssert, so in this case it is correct.

@rickie rickie requested review from Badbond and mussatto February 16, 2022 13:29
Copy link
Contributor

@mussatto mussatto left a comment

Choose a reason for hiding this comment

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

LGTM, I don't have anything extra to add other than the other discussions about the map assertions :)

Copy link
Member

@Badbond Badbond left a comment

Choose a reason for hiding this comment

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

Nice 👍 Got one question and one edge case to consider.

Copy link
Member

@Badbond Badbond left a comment

Choose a reason for hiding this comment

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

Thanks for the answers! 👍 LGTM.

@rickie
Copy link
Member Author

rickie commented Mar 3, 2022

Suggested commit message:

Introduce `AssertJMapTemplates` and assorted AssertJ templates

@Stephan202 Stephan202 added this to the 0.1.0 milestone Apr 10, 2022
@Stephan202 Stephan202 force-pushed the rossendrijver/assertj_template_improvements branch from ddbf8c7 to 0e42b2c Compare April 10, 2022 18:17
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.

Rebased and added a commit.

Tweaked suggested commit message:

Introduce assorted AssertJ Refaster templates (#37)

(The fact that one of the files is new doesn't seem so relevant.)

@mussatto anything else from your side? :)

}

@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
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
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
@UseImportPolicy(STATIC_IMPORT_ALWAYS)

return ImmutableSet.of(
assertThat((byte) 1 % 2).isEqualTo(1),
assertThat(Byte.valueOf((byte) 1) % 2).isEqualTo(1),
assertThat((int) 1 % 2).isEqualTo(1),
Copy link
Member

Choose a reason for hiding this comment

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

int is the default.

Suggested change
assertThat((int) 1 % 2).isEqualTo(1),
assertThat(1 % 2).isEqualTo(1),

assertThat(Byte.valueOf((byte) 1) % 2).isEqualTo(1),
assertThat((int) 1 % 2).isEqualTo(1),
assertThat(Integer.valueOf(1) % 2).isEqualTo(1),
assertThat((long) 1 % 2).isEqualTo(1),
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
assertThat((long) 1 % 2).isEqualTo(1),
assertThat(1L % 2).isEqualTo(1),

assertThat((int) 1 % 2).isEqualTo(1),
assertThat(Integer.valueOf(1) % 2).isEqualTo(1),
assertThat((long) 1 % 2).isEqualTo(1),
assertThat(Long.valueOf(1L) % 2).isEqualTo(1),
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
assertThat(Long.valueOf(1L) % 2).isEqualTo(1),
assertThat(Long.valueOf(1) % 2).isEqualTo(1),

@@ -221,4 +225,60 @@ private AssertJNumberTemplates() {}
return numberAssert.isNotNegative();
}
}

static final class AssertThatIsOdd {
Copy link
Member

Choose a reason for hiding this comment

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

Related to a discussion in another PR: I suppose this should be AssertThatNumberIsOdd. One rule could be to identify method invocations comprising multiple overloads, and to add parameter type(s) in those cases.


@AfterTemplate
@UseImportPolicy(ImportPolicy.STATIC_IMPORT_ALWAYS)
NumberAssert<?, ?> after(Integer number) {
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 just use long here :)

Comment on lines 230 to 233
@BeforeTemplate
AbstractIntegerAssert<?> before(byte number) {
return assertThat(number % 2).isEqualTo(1);
}
Copy link
Member

Choose a reason for hiding this comment

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

Since this calls assertThat(int), and since Refaster is smart enough to match byte to int, we can leave out this case. Same for short below.

@rickie
Copy link
Member Author

rickie commented Apr 11, 2022

Changes LGTM.

@Stephan202 Stephan202 merged commit eaa98c9 into master Apr 11, 2022
@Stephan202 Stephan202 deleted the rossendrijver/assertj_template_improvements branch April 11, 2022 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

6 participants