Skip to content

Verify that all (base-) classes declaring @ClassRules are public #764

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

Merged
merged 1 commit into from
Nov 20, 2013

Conversation

ferstl
Copy link
Contributor

@ferstl ferstl commented Nov 15, 2013

An IllegalAccessException is thrown in case a test class derives from a non-public base class which declares a @ClassRule.

For example, this code ...

 abstract class BaseTest {
   @ClassRule
   public static ExpectedException expEx = ExpectedException.none();
 }

 public class RealTest extends BaseTest {
   @Test
   public void myTest() {
     ...
   }
 }

… will cause this exception

 java.lang.RuntimeException: How did getFields return a field we couldn't access?
    at org.junit.runners.model.TestClass.getAnnotatedFieldValues(TestClass.java:161)
    ...
  Caused by: java.lang.IllegalAccessException: Class org.junit.runners.model.FrameworkField can not access a member of class BaseTest with modifiers "public static final"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:95)
    ...

To solve this issue, I added an additional verification to RuleFieldValidator that checks that all @ClassRules are declared in public classes. A similar validation is already done for @BeforeClass and @AfterClass methods.

@@ -61,6 +61,11 @@ public Field getField() {
public Class<?> getType() {
return fField.getType();
}

@Override
public Class<?> getDeclaringClass() {
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice if getDeclaringClass() returns a TestClass object, which is JUnit's wrapper for Class objects. Then you can add an isPublic() method to TestClass and avoid the duplicate Modifier.isPublic(aClass.getModifiers())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, that would make the checks simpler. However, TestClass does quite a few things in its constructor which are not necessary whatsoever to fix this bug. Are you sure you want to have getDeclaringClass() return a TestClass?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sure :-), but I would like to hear at least @dsaff's or @kcooney's position.

@stefanbirkner
Copy link
Contributor

Just two suggestions for improvement. Everything else is fine. Thanks @ferstl.

@ferstl
Copy link
Contributor Author

ferstl commented Nov 19, 2013

@stefanbirkner I made two new commits for each of your comments. I'll squash them if these changes are OK for everyone.
One more thing about FrameworkMember#getDeclaringClass() returning a TestClass: I first tried to initialize the declaring class in the constructors of FrameworkField and FrameworkMethod and just return them in each call to getDeclaringClass(). However, this resulted in an StackOverflowError during construction of the TestClass. So getDeclaringClass() does now create a new TestClass instance on each call. I don't think that is a good idea. I'd still prefer getDeclaringClass() returning a simple Class object.

@dsaff
Copy link
Member

dsaff commented Nov 20, 2013

I would indeed really like not to call the expensive TestClass constructor in multiple places. I agree that going back to getDeclaringClass() returning a simple Class object is the right approach.

Sorry I wasn't able to jump into the discussion before now.

@ferstl
Copy link
Contributor Author

ferstl commented Nov 20, 2013

I would indeed really like not to call the expensive TestClass constructor in multiple places. I agree that going back to getDeclaringClass() returning a simple Class object is the right approach.

@dsaff Ok, I removed that change. getDeclaringClass() does now return a plain Class object again.

@dsaff
Copy link
Member

dsaff commented Nov 20, 2013

Looks good to me. Please squash!

- Add the new method getDeclaringClass() to FrameworkMember
  - FrameworkMethod will return the class where the method is actually
    declared
  - FrameworkField will return the class where the field is actually
    declared
- Add validation logic to RuleFieldValidator
- Add test for validation logic
@ferstl
Copy link
Contributor Author

ferstl commented Nov 20, 2013

@dsaff Done.

dsaff pushed a commit that referenced this pull request Nov 20, 2013
Verify that all (base-) classes declaring @classrules are public
@dsaff dsaff merged commit 1029290 into junit-team:master Nov 20, 2013
@dsaff
Copy link
Member

dsaff commented Nov 20, 2013

Thanks!

@kcooney
Copy link
Member

kcooney commented Dec 24, 2013

Just catching up on (very) old JUnit updates.

I think returning a TestClass is reasonable. We could revisit the idea of having TestClass having a creational method that uses a cached map of Class to TestClass. Since runners live for the live of the JVM, and runners keep references to the classes they run, in practice I think we keep TestClass instances around forever, so it might not be so bad to keep a map.

@dsaff What do you think of caching a map from Class to TestClass?

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.

4 participants