-
Notifications
You must be signed in to change notification settings - Fork 432
Add some AllianceTracker unit tests #13835
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
base: main
Are you sure you want to change the base?
Conversation
frigoref
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JMoravec Thanks for the PR so far. There are some points that need to be addressed.
Please let me know if there is anything that needs clarification.
| public Set<GamePlayer> getPlayersInAlliance(final String allianceName) { | ||
| return alliances.entries().stream() | ||
| .filter(e -> e.getValue().equals(allianceName)) | ||
| .filter(e -> e.getValue() != null && e.getValue().equals(allianceName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should ensure before creating an entry to avoid null values not at every place where we would like to access such a value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been awhile since I've done Java, is the standard to have a @nonnull annotation (the lombok one maybe?) and raise an exception on instantiation if the input is null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another good point for our documentation. Generally we avoid those annotations unless it is violating the general design assumption from above (null only in local scope).
Want to submit a PR on the documentation update as well? 😇
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been awhile since I've done Java, is the standard to have a @nonnull annotation (the lombok one maybe?) and raise an exception on instantiation if the input is null?
@nonnull indicates a never null value. IE: something you don't need to check for nullity, because it simply just wont' be null.
and raise an exception on instantiation if the input is null?
Depends where the input is coming from. Ideally you would just guarantee the input to never be null and you can skip the null check.
The modern "rules-of-thumb" for java nullity I would summarize as:
- never return null
- never pass null
- never let
null"escape" the local scope of a method.
TripleA was written well before those good rules-of-thumb were a thing.
Concretely:
-
if a null value injected here will go on to cause a NullPointerException later, then we do want a null check here that throws. That is the "fail early principle", detect error conditions as early as you can and fail when those conditions are detected.
-
otherwise, the question is, have we dealt with all of the callers? Why can the passed value be null? Those callers are violating "never pass null", so they should each in turn be fixed. Short of that, we have to just deal with null values until we can properly remove the usage of nulls across method boundaries.
| final GamePlayer chretian = gameData.getPlayerList().getPlayerId("chretian"); | ||
| final AllianceTracker allianceTracker = gameData.getAllianceTracker(); | ||
| final RelationshipTracker relationshipTracker = gameData.getRelationshipTracker(); | ||
| // Validate that the expected setup for the alliance is what we expect from the data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second half of the comment is redundant, the assert-methods clearly express a validation.
If you see a comment here as valuable, I suggest shortening it to "Validate the expected setup for the alliance"
| } | ||
|
|
||
| @Test | ||
| void testAllianceTrackerState() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to a specific name giving information about the exact test method / test case, e.g., testAddToAllianceWithTwoPlayerToSameAlliance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @frigoref 's feedback here. The heuristic is, based on the knowledge that this test has failed, and the test name, would you know why it failed?
|
|
||
| // No alliances exist on creation | ||
| assertTrue(allianceTracker.getAlliances().isEmpty()); | ||
| allianceTracker.addToAlliance(player1, allianceName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest to have a separate block for the method that is actually tested to visually separate it from the data preparation before and the validation/checks after.
| assertTrue(allianceTracker.getAlliances().isEmpty()); | ||
| allianceTracker.addToAlliance(player1, allianceName); | ||
| allianceTracker.addToAlliance(player2, allianceName); | ||
| assertEquals(1, allianceTracker.getAlliances().size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A local variable definition for allianceTracker.getAlliances() (e.g. alliances) could help structure the checks.
Later also allianceTracker.getAllies(player1) -> player1Allies and getPlayersInAlliance(allianceName) playersInAlliance.
This makes it easier to read the actual validation/check done via the assert-method and also structures the code into blocks improving again the readability.
| } | ||
|
|
||
| @Test | ||
| void testAllianceTrackerNullAlliance() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting back to the above state design goal of not having any null values passed around (only in the scope of one method), I assume this test method is not needed.
Note, that we do not want to make each method bullet-proof as we are the ones using them. Hence, we can rely on general design decisions like this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguably this method could throw a an explicit NullPointerException due to the null input:
allianceTracker.addToAlliance(player1, null);
| import static org.hamcrest.Matchers.is; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| import static org.junit.jupiter.api.Assertions.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is causing one of several failed checks for the PR
> Task :game-app:domain-data:check
[ant:checkstyle] [WARN] /home/runner/work/triplea/triplea/game-app/game-core/src/test/java/games/strategy/engine/data/AllianceTrackerTest.java:5:47: Using the '.*' form of import should be avoided - org.junit.jupiter.api.Assertions.*. [AvoidStarImport]
Please make sure all checks are passed and if not provide additional commits to correct them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't notice this change locally when pushing up and assumed when I ran spotlessCheck it would fix things like this. I probably need to change an InetlliJ setting so it doesn't auto import *'s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. And we should probably add this to the setup guide ad well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, looks missing: https://github.com/triplea-game/triplea/blob/master/docs/development/how-to/ide-setup/intellij-setup.md
AFAIK the way to do fix the star import is to update a setting in intellij that is basically "how many imports before using a start", and update that value to '999'
DanVanAtta
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few comments, it's good to see the contributions.
@frigoref / @JMoravec , I recommend checking out this video:
Why Good Developers Write Bad Tests
https://www.youtube.com/watch?v=hM_ex4-xu4E
After the video, a number of fixes to the test cases become a lot more obvious IMO, strongly recommend the video to you both!
| public Set<GamePlayer> getPlayersInAlliance(final String allianceName) { | ||
| return alliances.entries().stream() | ||
| .filter(e -> e.getValue().equals(allianceName)) | ||
| .filter(e -> e.getValue() != null && e.getValue().equals(allianceName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been awhile since I've done Java, is the standard to have a @nonnull annotation (the lombok one maybe?) and raise an exception on instantiation if the input is null?
@nonnull indicates a never null value. IE: something you don't need to check for nullity, because it simply just wont' be null.
and raise an exception on instantiation if the input is null?
Depends where the input is coming from. Ideally you would just guarantee the input to never be null and you can skip the null check.
The modern "rules-of-thumb" for java nullity I would summarize as:
- never return null
- never pass null
- never let
null"escape" the local scope of a method.
TripleA was written well before those good rules-of-thumb were a thing.
Concretely:
-
if a null value injected here will go on to cause a NullPointerException later, then we do want a null check here that throws. That is the "fail early principle", detect error conditions as early as you can and fail when those conditions are detected.
-
otherwise, the question is, have we dealt with all of the callers? Why can the passed value be null? Those callers are violating "never pass null", so they should each in turn be fixed. Short of that, we have to just deal with null values until we can properly remove the usage of nulls across method boundaries.
| import static org.hamcrest.Matchers.is; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| import static org.junit.jupiter.api.Assertions.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, looks missing: https://github.com/triplea-game/triplea/blob/master/docs/development/how-to/ide-setup/intellij-setup.md
AFAIK the way to do fix the star import is to update a setting in intellij that is basically "how many imports before using a start", and update that value to '999'
| } | ||
|
|
||
| @Test | ||
| void testAllianceTrackerNullAlliance() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguably this method could throw a an explicit NullPointerException due to the null input:
allianceTracker.addToAlliance(player1, null);
| } | ||
|
|
||
| @Test | ||
| void testAllianceTrackerState() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @frigoref 's feedback here. The heuristic is, based on the knowledge that this test has failed, and the test name, would you know why it failed?
Just some additional explicit tests done on the AllianceTracker class. Only functional thing that's added is a null check on the stream, as a "null" alliance here will cause a NullPointerException.
Let me know if that's actually expected on that method. I can remove/add a comment there.