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

2.x class method visibility #916

Closed
wants to merge 28 commits into from

Conversation

epeee
Copy link
Member

@epeee epeee commented Feb 8, 2017

Check List:

a very first draft for class and method visibility.

e.g.

  @Test
  public void test1() {
    Assertions.assertThat(ArrayList.class).hasDeclaredPublicMethods("size", "grow", "add" , "fastRemove");
  }

would end up in:

java.lang.AssertionError: 
Expecting
  <java.util.ArrayList>
to have public methods:
  <["size", "grow", "add", "fastRemove"]>
but the following are not public:
  <{"fastRemove"="private", "grow"="private"}>

@joel-costigliola
Copy link
Member

@epeee, just a quick comment (I haven't reviewed the PR yet) can you put to have declared public methods instead of to have public methods for hasDeclaredPublicMethods assertion error message ?

* assertThat(String.class).isPublic();
* assertThat(Math.class).isPublic();
*
* // This assertion fail:
Copy link
Member

Choose a reason for hiding this comment

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

typo: fails

*
* <pre><code class='java'> public class MyClass { }
*
* // This assertion succeed:
Copy link
Member

Choose a reason for hiding this comment

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

succeeds

@@ -284,4 +329,111 @@ public SELF hasDeclaredFields(String... fields) {
classes.assertHasDeclaredFields(info, actual, fields);
return myself;
}

/**
* Verifies that the actual {@code Class} has the {@code methods}.
Copy link
Member

Choose a reason for hiding this comment

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

reword: has the given methods. I use {@code for something that denotes a java element like {@code true} or {@code equals}

*
* @since 2.7.0 / 3.7.0
*/
public SELF hasMethods(String... methods) {
Copy link
Member

Choose a reason for hiding this comment

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

minor refactoring: I think we should rename methods to methodNames so that it is clear that we are not dealing with the Method type.

The example would be better with a superclass for MyClass having a public method and use it in the successful assertion example.

* assertThat(MyClass.class).hasDeclaredMethods("methodOne", "methodTwo");
*
* // This one should fail :
* assertThat(MyClass.class).hasDeclaredMethods("methodThree");</code></pre>
Copy link
Member

Choose a reason for hiding this comment

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

I think a better example would show a superclass for MyClass with a public method and use it in the failed assertion example (keep the example of the non existent method)

* assertThat(MyClass.class).hasPublicMethods("methodOne");
* assertThat(MyClass.class).hasPublicMethods("methodOne", "methodTwo");
*
* // This one should fail :
Copy link
Member

Choose a reason for hiding this comment

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

plural

/**
* Verifies that the actual {@code Class} has the declared public {@code methods}.
*
* <pre><code class='java'> class MyClass {
Copy link
Member

Choose a reason for hiding this comment

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

same comment as before: introduce a superclass

public class ClassModifierShouldBe extends BasicErrorMessageFactory {

private ClassModifierShouldBe(Class<?> actual, boolean positive, String modifier) {
super("%nExpecting:%n <%s>%n" + (positive ? "to" : "not to") + " be a %s class.", actual, modifier);
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: finish the error message with but was % and add actual's modifier value ?

*/
public void assertHasDeclaredMethods(AssertionInfo info, Class<?> actual, String... methods) {
assertNotNull(info, actual);
doAssertHasMethods(info, actual, actual.getDeclaredMethods(), methods);
Copy link
Member

Choose a reason for hiding this comment

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

how do you now in doAssertHasMethods that the error message is about declared methods and not methods ?

Copy link
Member Author

Choose a reason for hiding this comment

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

thx for pointing that out. You are right, this one was still missing but should be fixed now.

private static boolean noNonMatchingModifier(Set<String> expectedMethodNames, Map<String, Integer> actualMethods, Map<String, String> nonMatchingModifiers, int modifier) {
for (String method : actualMethods.keySet()) {
if(expectedMethodNames.contains(method)) {
if ((actualMethods.get(method) & modifier) == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

I believe you can combine if conditions

}
}

private static boolean noNonMatchingModifier(Set<String> expectedMethodNames, Map<String, Integer> actualMethods, Map<String, String> nonMatchingModifiers, int modifier) {
Copy link
Member

Choose a reason for hiding this comment

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

rename actualMethods to better express what this map contains (methodsModifer ?)

}

@Test
public void should_pass_if_no_methods_are_expected() {
Copy link
Member

Choose a reason for hiding this comment

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

When no methods are given, the assertion should only pass if the actual class has not methods of the expected category (that is consistent with contains() assertion that succeeds it the actual iterable/array is empty).

This should be true whatever the category of expected methods is (public, declared, ...)

Copy link
Member Author

Choose a reason for hiding this comment

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

k, thx. I will add this one.

Copy link
Member Author

@epeee epeee Feb 12, 2017

Choose a reason for hiding this comment

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

To sum it up:
When no methods are given

  • hasPublicMethods will always fail because of methods inherit from Object
  • hasDeclaredPublicMethods will pass if no public methods are declared in the given class
  • hasDeclaredMethods will pass if no methods are declared in the given class
  • hasMethods also will always fail because of methods inherit from Object

Am I right?

@epeee
Copy link
Member Author

epeee commented Feb 12, 2017

@joel-costigliola thx for the feedback. I have integrated your suggestions.

@joel-costigliola
Copy link
Member

Thanks @epeee, I'm currently polishing the code, will be integrated soonish.

hasMethods only checks public methods, I believe this is an error as we have an hasPublicMethods assertion, have I missed something ?

@joel-costigliola
Copy link
Member

joel-costigliola commented Feb 13, 2017

Another thing, can you (in the future) rebase your branches instead of merging the main branch (2.x in our case) into yours ?
The reason is that it is easier for me to keep a linear history with one commit per feature (most of the time at least). I took care of this branch but that involved a lot a cherry pick and few conflicts to solve.

@epeee
Copy link
Member Author

epeee commented Feb 13, 2017

@joel-costigliola k, I will do so in the future (rebase).

ad hasMethods only checks public methods:
good catch, the isEmptyAndHasNoMethodsWithModifier check is currently done just for public methods (wrongly).

@joel-costigliola
Copy link
Member

@epeee thanks for the additional commit.

I was trying to run the tests and found some were flaky because ShouldHaveMethods does not enforce a predictable order in the set of methods it receives, for example this test fails sometimes:

    thrown.expectAssertionError(shouldNotHaveMethods(actual, true,
             newLinkedHashSet("publicMethod", "privateMethod", "protectedMethod")));
    classes.assertHasDeclaredMethods(someInfo(), actual);

because the order of methods is not always the same (a LinkedHashSet ensure).

The fix is simple, use SortedSet instead of Set in ShouldNotHaveMethods (it will also sort methods alphabetically which is gonna be helpful for users).

One more thing, could you add some tests for ShouldNotHaveMethods, you can follow ShouldHaveFields_create_Test as an example.

One last thing (promised !) could you format the constructors in ShouldHaveMethods so that it goes to a new line after a %n like in ShouldHaveSuppressedException:

  private ShouldHaveSuppressedException(Throwable actual, Throwable expectedSuppressedException) {
    super("%n" +
          "Expecting:%n" +
          "  <%s>%n" +
          "to have a suppressed exception with the following type and message:%n" +
          "  <%s> / <%s>%n" +
          "but could not find any in actual's suppressed exceptions:%n" +
          "  <%s>.",
          actual, expectedSuppressedException.getClass().getName(), expectedSuppressedException.getMessage(),
          actual.getSuppressed());
  }

it makes the error message easier to read.

thanks !

@epeee
Copy link
Member Author

epeee commented Feb 20, 2017

@joel-costigliola thx for your feedback. I've already updated the pr.

@joel-costigliola
Copy link
Member

Integrated thanks !

@epeee epeee deleted the 2.x_class_method_visibility branch March 3, 2017 20:24
@joel-costigliola
Copy link
Member

joel-costigliola commented Apr 1, 2017

@epeee I think it would be better to align fields and methods assertions, I'm thinking of reducing methods assertions to:

  • hasMethods(String... methodNames) : check methods (whatever visibility)
  • hasPublicMethods (String... methodNames)` : check public accessible methods
  • hasDeclaredMethods(String... methodNames) : check declared methods (includes non public ones)
    I want to do so to be consistent with the fields assertions
  • hasPublicFields(String... fieldNames) : check public accessible fields
  • hasDeclaredFields(String... fieldNames) : check declared fields (includes non public ones)

I might consider renaming hasMethods to hasPublicMethods and hasFields to hasPublicFields to avoid ambiguity.

WDYT ?

@epeee
Copy link
Member Author

epeee commented Apr 2, 2017

@joel-costigliola: 👍
actually, it looks like the javadoc for org.assertj.core.api.AbstractClassAssert#hasMethods is wrong (the sample using the private method).
edit: just realized that you already fixed the doc for 2.x 👍

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.

None yet

2 participants