|
|
| Re: E4 DI in unittests? [message #900680 is a reply to message #900651] |
Wed, 08 August 2012 03:21   |
 |
Markus Oley Messages: 227 Registered: July 2009 Location: Germany |
Senior Member |
|
|
Hi again,
my problem is the following:
I use DI to inject my own objects.
For example:
@Creatable
class A {
@Inject private B someField;
@Inject private C someOtherField;
}
@Creatable
class B {
@Inject C thirdField;
}
So from scratch, A.B.C is null, and I can't set it from outside.
OK, I could use mocking, but I cannot mock a private field with Mockito, I guess, so I have to make it at least package private.
Better would be to get these things injected, I think. This would simplify the test itself.
Could you give me a hint, what's best practice in this context?
Thanks a lot.
Best regards
Markus
|
|
|
|
|
| Re: E4 DI in unittests? [message #900788 is a reply to message #900680] |
Wed, 08 August 2012 09:35   |
Joseph Carroll Messages: 156 Registered: May 2012 Location: Milwaukee, WI |
Senior Member |

|
|
To build off of your example here:
Markus Oley wrote on Wed, 08 August 2012 02:21
@Creatable
class A {
@Inject private B someField;
@Inject private C someOtherField;
}
@Creatable
class B {
@Inject C thirdField;
}
Just be very careful that there isn't a circular dependency such as:
@Creatable
class C {
@Inject /*qualifier*/ A fourthField;
}
From a java standpoint this certainly does not create any issues. The problem is if there aren't any usable instance of A / B / C in the context, injection will fail. (I don't know if there will be any listed error/warning)
If you need/want the fields to be private for some reason, you can use the same approach the DI engine uses with reflection for the mock objects. (See the code below or the attached file. But in general I agree with Tom, (whether using DI or not) if I need to test a field in a unit test, I make it package-private.
Also, the code below is only a sample and isn't quite 'ready to go'. A couple of things still need to be implemented, but it will at least get you started.
JD
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class MockInjector {
public Object getInstance(Class<?> testClass) {
Object instance = null;
try {
instance = testClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (instance == null) {
Constructor<?>[] constructs = testClass.getDeclaredConstructors();
// choose a constructor
Class<?>[] params = constructs[0].getParameterTypes();
Object[] paramInstances = new Object[params.length];
// need to create logic to provide parameters for desired constructor
try {
instance = constructs[0].newInstance(paramInstances);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return instance;
}
public void mockInject(Object testClassInstance, String fieldName, Object fieldValue) {
Class<?> clazz = testClassInstance.getClass();
Field testField = null;
try {
testField = clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
/*
* It doesn't matter if the field is private or public, by
* invoking setAccessible(true) it removes the accessibility
* checks performed by the JVM by setting a flag. It does not
* remove the security checks. Effectively this eliminates
* encapsulation.
*/
testField.setAccessible(true);
try {
testField.set(testClassInstance, fieldValue);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.04813 seconds