This question already has answers here:
How to add test coverage to a private constructor?
(18 answers)
Closed 5 years ago.
If a class contains a bunch of static methods, in order to make sure no one by mistake initializes an instance of this class, I made a private constructor:
private Utils() {
}
Now .. how could this be tested, given that constructor can't be seen? Can this be test covered at all?
Using reflection, you can invoke a private constructor:
Constructor<Util> c = Utils.class.getDeclaredConstructor();
c.setAccessible(true);
Utils u = c.newInstance(); // Hello sailor
However, you can make even that not possible:
private Utils() {
throw new UnsupportedOperationException();
}
By throwing an exception in the constructor, you prevent all attempts.
I would make the class itself final too, just "because":
public final class Utils {
private Utils() {
throw new UnsupportedOperationException();
}
}
Test the intent of the code .. always :)
For example: If the point of the constructor being private is to not be seen then what you need to test is this fact and nothing else.
Use the reflection API to query for the constructors and validate that they have the private attribute set.
I would do something like this:
#Test()
public void testPrivateConstructors() {
final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
}
}
If you want to have a proper test for the object construction, you should test the public API which allows you to get the constructed object. That's the reason the said API should exist: to build the objects properly so you should test it for that :).
#Test
public//
void privateConstructorTest() throws Exception {
final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors();
// check that all constructors are 'private':
for (final Constructor<?> constructor : constructors) {
Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers()));
}
// call the private constructor:
constructors[0].setAccessible(true);
constructors[0].newInstance((Object[]) null);
}
to make sure no one by mistake initializes an instance of this class
Usually what I do, is to change the method/constructor from private to default package visibility. And I use the same package for my test class, so from the test the method/constructor is accessible, even if it is not from outside.
To enforce the policy to not instantiate the class you can:
throw UnsupportedOperationException("don't instantiate this class!") from the default empty constructor.
declare the class abstract: if it only contains static methods, you can call the static methods but not instantiate it, unless you subclass it.
or apply both 1+2, you can still subclass and run the constructor if your test shares the same package as the target class.
This should be quite "error proof"; malicious coders will always find a workaround :)
If you have a private constructor, it is called from some not-so-private method of your code. So you test that method, and your constructor is covered. There's no religious virtue in having a test per method. You are looking for function or better yet branch coverage level, and you can get that simply by exercising the constructor through the code path that uses it.
If that code path is convoluted and hard to test, perhaps you need to refactor it.
If you add an exception in the constructor such as:
private Utils() {
throw new UnsupportedOperationException();
}
The invocation of constructor.newInstance() in the test class will throw an InvocationTargetException instead of your UnsupportedOperationException, but the desired exception will be contained in the thrown one.
If you want to assert the thrown of your exception, you could throw the target of the invocation exception, once the invocation exception has been caught.
For instance, using jUnit 4 you could do this:
#Test(expected = UnsupportedOperationException.class)
public void utilityClassTest() throws NoSuchMethodException, IllegalAccessException, InstantiationException {
final Constructor<Utils> constructor = Utils.class.getDeclaredConstructor();
constructor.setAccessible(true);
try {
constructor.newInstance();
} catch (InvocationTargetException e) {
throw (UnsupportedOperationException) e.getTargetException();
}
}
Don't. The constructor is private. That's all you need. Java enforces its privacy.
Don't test the platform.
Related
I am trying to write a java program where the user enters a testclass and then my program make sure it is a testclass. That is, checking for constructor without parameter, and testing the methods so that they are working (return true or false).
Now, what I am confused about is this API reflection. I am trying to follow the tutorial located at https://docs.oracle.com/javase/tutorial/reflect/index.html but I am getting confused and I am wondering if anyone could explain it and maybe help me get started. In the tutorial it says for example how to get the testclass, but you can do it in so many different ways. I tried using the
...
Class c = str.getClass();
...
where str is the name of the class I want to look at. How do I proceed from here to obtain information about the class's methods, constructor and their parameters? I simply just want examples and explaining. Not a fully written program. Thanks in advance.
Firstly, using Class c = str.getClass(); won't do what you want. It will return the class of the str object, which is just String. What you should do instead is use Class.forName(str); note that this will require the fully qualified name of the class.
Once you have the class object, you can use it's methods to get the relevant information. It sounds like the most useful to you would be .getConstructors(). With that, you could use (for example) the .getParameterCount() method of the Constructors to find any constructors which take no parameters. For more information, I'd advice looking at the documentation.
Retrieving class
Class.forName(str) will return the class with the given name. You must pass the fully qualified name, like "org.example.MyTestSuite". In other use cases where you want to create another instance of a given object, you can just call Object#getClass().
Constructors and Instantiation
You can get all constructor with Class#getConstructors(), so that you could check if a nullary constructor (without parameters) is available. Class#newInstance() will try to create an instance with the nullary constructor and throw an IllegalAccessException if none is available. Constructors with parameters can be invoked with Constructor#newInstance(Object...).
Methods
A class' methods will be listed with Class#getDeclaredMethods(). For further examination Method#getGenericParameterTypes() returns the parameters' classes. You can even make private methods invokable by using Method#setAccessible(true). Then finally Method#invoke(Class) executes the method on the given class instance. Use Method#invoke(Class, Object...) for methods with arguments, whereas the var-args represents the arguments.
Example
The Java Documentation contains some good examples, I modified one a little bit for your use case:
try {
// retrieving class
Class<?> c = Class.forName(str);
// will throw IllegalAccessException if the class
// or its nullary constructor is not accessible:
Object t = c.newInstance();
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
// run only test methods
if (!mname.startsWith("test")) {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if (pType.length != 0) {
throw new RuntimeException("Test methods must not have parameters.");
}
try {
// you can call private methods by setting this flag
m.setAccessible(true);
// invoking method m of instance t
m.invoke(t);
} catch (InvocationTargetException x) {
// Handle any exceptions thrown by method to be invoked.
Throwable cause = x.getCause();
err.format("invocation of %s failed: %s%n",
mname, cause.getMessage());
}
}
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
You can just invoke the appropriate methods from the Class class found Here. Namely, getDeclaredFields() and getDeclaredMethods()
How to restrict developers to use reflection to access private methods and constructors in Java?
Using normal Java code we can't access private constructors or private methods outside of a class. But by using reflection we can access any private methods and constructors in a Java class.
So how can we give security to our Java code?
Run your application using a SecurityManager and a sufficiently restrictive security policy.
There's a short summary in the tutorial and extensive information in the security documentation.
Add checkPermission() method in all of your private method/constructor.
checkPermission using sun.reflect.Reflection.getCallerClass(int n) by assert callerClass=selfClass.
The getCallerClass returns the class of the method realFramesToSkip frames up the stack (zero-based), ignoring frames associated with java.lang.reflect.Method.invoke() and its implementation. The first frame is that associated with this method, so getCallerClass(0) returns the Class object for sun.reflect.Reflection.
public class PrivateConstructorClass {
private PrivateConstructorClass() {
checkPerMission();
//you own code go below
}
void checkPerMission() {
Class self = sun.reflect.Reflection.getCallerClass(1);
Class caller = sun.reflect.Reflection.getCallerClass(3);
if (self != caller) {
throw new java.lang.IllegalAccessError();
}
}
}
You can try to test reflect, it will fail:
public class TestPrivateMain {
Object newInstance() throws Exception {
final Class<?> c = Class.forName("package.TestPrivate");
final Constructor<?> constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
}
public static void main(String[] args) throws Exception {
Object t = new TestPrivateMain().newInstance();
}
}
You (as the developer of the code in question) cannot do that.
The end user, who runs the application, could install a SecurityManager that forbids reflection.
Let's say I have a class of Books which can be instantiate using a constructor like this:
class Book {
public Book(String name) {
Constructor<Book> cons = null;
try {
cons = Book.class.getConstructor(String.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
//Additional detail. I will pass the 'cons' to Hibernate's constructor validation
}
}
Inside the constructor Book(String name), I have a need to get the reference to the constructor itself. But the problem is I have to wrap the statement with a pointless try-catch block as I know the constructor definitely exists and it sure is accessible. So I would like to know if Java provides a way to get the reference to the constructor inside itself without having to check for those Exceptions.
IMHO, you can't get rid of the try-catch.
The "class.getConstructor" is not sure about the availability of the constructor you are querying, so it doesnt matter whether you are sure about the availability or not.
However,you may try google reflections instead. Here is the javadoc
I didnot use it before, but I do see a method for getting all constructors. and there is no "throws"
public static Set<Constructor> getAllConstructors(Class<?> type,
com.google.common.base.Predicate<? super Constructor>... predicates)
get all constructors of given type, up the super class hierarchy, optionally filtered by predicates
Effective Java (Second Edition), Item 4, discusses using private constructors to enforce noninstantiability. Here's the code sample from the book:
public final class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
However, AssertionError doesn't seem like the right thing to throw. Nothing is being "asserted", which is how the API defines the use of AssertionError.
Is there a different Throwable that's typically in this situation? Does one usually just throw a general Exception with a message? Or is it common to write a custom Exception for this?
It's pretty trivial, but more than anything I guess I'm just curious about it from a style and standards perspective.
There is an assertion: "I'm asserting that this constructor will never be called". So, indeed, AssertionError is correct here.
I like including Bloch's comment:
// Suppress default constructor for noninstantiability
Or better yet putting it in the Error:
private UtilityClass()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
UnsupportedOperationException sounds like the best fit, though a checked exception would be even better, since it might warn someone erroneously instantiating the class at compile time.
What about IllegalAcessError ? :)
When the code requires the inclusion of the JUnit as a dependency such as within the maven test scope <scope>test</scope>, then go straight to Assertion.fail() method and benefit from significant improvement in clarity.
public final class UtilityClass {
private UtilityClass() {
fail("The UtilityClass methods should be accessed statically");
}
}
When outside the test scope, you could use something like the following, which would require a static import to use like above. import static pkg.Error.fail;
public class Error {
private static final Logger LOG = LoggerFactory.getLogger(Error.class);
public static void fail(final String message) {
LOG.error(message);
throw new AssertionError(message);
// or use your preferred exception
// e.g InstantiationException
}
}
Which the following usage.
public class UtilityClassTwo {
private UtilityClassTwo() {
Error.fail("The UtilityClass methods should be accessed statically");
}
}
In its most idiomatic form, they all boil down to this:
public class UtilityClassThree {
private UtilityClassThree() {
assert false : "The UtilityClass methods should be accessed statically";
}
}
One of the built in exceptions, UnsupportedOperationException can be thrown to
indicate that 'the requested operation is not supported'.
private Constructor() {
throw new UnsupportedOperationException(
"Do not instantiate this class, use statically.");
}
No no no, with all due respect to Josh Bloch, never throw an AssertionError unless it's from an assertion. If you want an AssertionError here, throw it with assert(false). Then someone reading the code can find it later.
Even better, define your own exception, say CantInstantiateUtilityClass. then you'll have code that says
try {
// some stuff
} catch (CantInstantiateUtilityClass e) {
// react
}
so that the reader of the catcher knows what happened.
Let me just note that the standard still defines AssertionError as the result of a failed assertion, not as what some beginner thinks ought to be thrown in place of a well-defined informative exception. Sadly, good exception discipline is perhaps the least encouraged skill in Java programming.
A broken assertion means that you've broken a contract specification of your code. So it's the right thing here.
However, as I assume you'll be privately instantiating an instance, it will also call the constructor and cause an error- unless you have another constructor?
You can create your own class extending Throwable, e.g.:
class NoninstantiabilityError extends Throwable
This has the following advantages:
The name indicates the problem
Because it directly extends Throwable it is unlikely that it will be caught by accident
Because it directly extends Throwable it is checked and calling the respective constructor by accident would require catching the exception
Usage example:
public final class UtilityClass {
private UtilityClass() throws NoninstantiabilityError {
throw new NoninstantiabilityError();
}
...
}
So, in Java, the first line of your constructor HAS to be a call to super... be it implicitly calling super(), or explicitly calling another constructor. What I want to know is, why can't I put a try block around that?
My specific case is that I have a mock class for a test. There is no default constructor, but I want one to make the tests simpler to read. I also want to wrap the exceptions thrown from the constructor into a RuntimeException.
So, what I want to do is effectively this:
public class MyClassMock extends MyClass {
public MyClassMock() {
try {
super(0);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// Mocked methods
}
But Java complains that super isn't the first statement.
My workaround:
public class MyClassMock extends MyClass {
public static MyClassMock construct() {
try {
return new MyClassMock();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public MyClassMock() throws Exception {
super(0);
}
// Mocked methods
}
Is this the best workaround? Why doesn't Java let me do the former?
My best guess as to the "why" is that Java doesn't want to let me have a constructed object in a potentially inconsistent state... however, in doing a mock, I don't care about that. It seems I should be able to do the above... or at least I know that the above is safe for my case... or seems as though it should be anyways.
I am overriding any methods I use from the tested class, so there is no risk that I am using uninitialized variables.
Unfortunately, compilers can't work on theoretical principles, and even though you may know that it is safe in your case, if they allowed it, it would have to be safe for all cases.
In other words, the compiler isn't stopping just you, it's stopping everyone, including all those that don't know that it is unsafe and needs special handling. There are probably other reasons for this as well, as all languages usually have ways to do unsafe things if one knows how to deal with them.
In C# .NET there are similar provisions, and the only way to declare a constructor that calls a base constructor is this:
public ClassName(...) : base(...)
in doing so, the base constructor will be called before the body of the constructor, and you cannot change this order.
It's done to prevent someone from creating a new SecurityManager object from untrusted code.
public class Evil : SecurityManager {
Evil()
{
try {
super();
} catch { Throwable t }
{
}
}
}
I know this is an old question, but I liked it, and as such, I decided to give it an answer of my own. Perhaps my understanding of why this cannot be done will contribute to the discussion and to future readers of your interesting question.
Let me start with an example of failing object construction.
Let's define a class A, such that:
class A {
private String a = "A";
public A() throws Exception {
throw new Exception();
}
}
Now, let's assume we would like to create an object of type A in a try...catch block.
A a = null;
try{
a = new A();
}catch(Exception e) {
//...
}
System.out.println(a);
Evidently, the output of this code will be: null.
Why Java does not return a partially constructed version of A? After all, by the point the constructor fails, the object's name field has already been initialized, right?
Well, Java can't return a partially constructed version of A because the object was not successfully built. The object is in a inconsistent state, and it is therefore discarded by Java. Your variable A is not even initialized, it is kept as null.
Now, as you know, to fully build a new object, all its super classes must be initialized first. If one of the super classes failed to execute, what would be the final state of the object? It is impossible to determine that.
Look at this more elaborate example
class A {
private final int a;
public A() throws Exception {
a = 10;
}
}
class B extends A {
private final int b;
public B() throws Exception {
methodThatThrowsException();
b = 20;
}
}
class C extends B {
public C() throws Exception { super(); }
}
When the constructor of C is invoked, if an exception occurs while initializing B, what would be the value of the final int variable b?
As such, the object C cannot be created, it is bogus, it is trash, it is not fully initialized.
For me, this explains why your code is illegal.
I can't presume to have a deep understanding of Java internals, but it is my understanding that, when a compiler needs to instantiate a derived class, it has to first create the base (and its base before that(...)) and then slap on the extensions made in the subclass.
So it is not even the danger of uninited variables or anything like that at all. When you try to do something in the subclass' constructor before the base class' constructor, you are basically asking the compiler to extend a base object instance that doesn't exist yet.
Edit:In your case, MyClass becomes the base object, and MyClassMock is a subclass.
I don't know how Java is implemented internally, but if the constructor of the superclass throws an exception, then there isn't a instance of the class you extend. It would be impossible to call the toString() or equals() methods, for example, since they are inherited in most cases.
Java may allow a try/catch around the super() call in the constructor if 1. you override ALL methods from the superclasses, and 2. you don't use the super.XXX() clause, but that all sounds too complicated to me.
I know this question has numerous answers, but I'd like to give my little tidbit on why this wouldn't be allowed, specifically to answer why Java does not allow you to do this. So here you go...
Now, keep in mind that super() has to be called before anything else in a subclass's constructor, so, if you did use try and catch blocks around your super() call, the blocks would have to look like this:
try {
super();
...
} catch (Exception e) {
super(); //This line will throw the same error...
...
}
If super() fails in the try block, it HAS to be executed first in the catch block, so that super runs before anything in your subclass`s constructor. This leaves you with the same problem you had at the beginning: if an exception is thrown, it isn't caught. (In this case it just gets thrown again in the catch block.)
Now, the above code is in no way allowed by Java either. This code may execute half of the first super call, and then call it again, which could cause some problems with some super classes.
Now, the reason that Java doesn't let you throw an exception instead of calling super() is because the exception could be caught somewhere else, and the program would continue without calling super() on your subclass object, and possibly because the exception could take your object as a parameter and try to change the value of inherited instance variables, which would not yet have been initialized.
One way to get around it is by calling a private static function. The try-catch can then be placed in the function body.
public class Test {
public Test() {
this(Test.getObjectThatMightThrowException());
}
public Test(Object o) {
//...
}
private static final Object getObjectThatMightThrowException() {
try {
return new ObjectThatMightThrowAnException();
} catch(RuntimeException rtx) {
throw new RuntimeException("It threw an exception!!!", rtx);
}
}
}