Open Saljack opened 3 years ago
Yes, the interface resolution method feature with @Tested
needs to be documented.
However, I am not convinced that using null
to inject constructor parameters of interface types is a good idea. Constructor injection is inferior to field injection. I am aware that the Spring framework documentation recommends the use of constructor injection, but that's only because (IMO) they don't want to concede that Java EE/CDI got it right with (annotated) field injection.
I don't see a difference between a null in field injection and a null in constructor injection. I think that JMockit should support both. JMockit has support for null in constructor but only for instances without @Tested
annotation so why not enable it for them too.
See this from TestedClassWithFullDITest.java :
static class ClassWithUnsatisfiableConstructor { ClassWithUnsatisfiableConstructor(@SuppressWarnings("unused") int someValue) {} }
static class ClassWithFieldToInject { ClassWithUnsatisfiableConstructor dependency; }
@Test
public void instantiateClassWithFieldToInjectWhoseTypeCannotBeInstantiated(@Tested(fullyInitialized = true) ClassWithFieldToInject cut) {
assertNotNull(cut);
assertNull(cut.dependency);
}
There are another ways how to do it. There should be another property in @Tested
annotation for example nullInject with default false and than you can enable it as @Tested(fullyInitialized = true, nullInject = true)
. Or instead null injections there can be mocks (but I am not able to find an easy way how to programmatically create these mocks) and then I can create a interfaceResolution
method. Something like:
@Tested
Class<?> interfaceResolution(Class<?> interfaceClass) {
return JMockit.createMock(interfaceClass);
}
I use the constructor injection because class with this injection type are easier to create and test. I don't have to use a third party testing framework like your great JMockit but I can create it easy in a simple unit test. I lose this possibility if I use the field injection with private fields. I create fields as private final
and then I use Lombok with @RequiredArgConstructor
annotation which generates constructor with these fields.
@RequiredArgsConstructor
class ClassWithConstructorInjection {
private final DependecyField dependencyField;
}
"create it easy in a simple unit test" is not the reality in typical real-world projects. The one I currently develop has lots of Spring DI beans having multiple dependencies on other beans, which in turn have their own injected dependencies. Wiring all that manually would be awful.
"create it easy in a simple unit test" is not the reality in typical real-world projects. The one I currently develop has lots of Spring DI beans having multiple dependencies on other beans, which in turn have their own injected dependencies. Wiring all that manually would be awful.
I completly agree with you but then we can discuss if the class is good designed. But you still have the opportunity to do it. I would like more discuss on the problems from this issue. Returning null for fullyInitialized @Tested
where is missing one dependency is definitely bug. There should be at least error message (because user don't know what happened without checking JMockit code).
Please provide the following information:
Version of JMockit that was used: 1.49
Description of the problem or enhancement request: I have class with many dependencies and I use injectiontion by constructor. I want to test one method which needs only some dependencies and other dependencies can be null. I think it is a good place where I can use fullyInitialized. It works greate with classes because these classes are initialized automaticaly but I use interfaces and JMockit is not able to instate these interfaces. But I do not need this interfaces and null are ok for my test. I do not want to create an
@Injectable
field for all dependencies because it makes no sense to me if I don't want to use it. I tried to play withProviding an interface resolution method
(by the way it should be documented in JMockit documentation) but it does not help because some interfaces are runtime generated (Spring Data Repository). Here is an example how I tried it:So I checked JMockit code and the commit b77f430 Changed the injection behavior into @tested(fullyInitialized) objects, so that non-required fields that cannot be injected don't cause failure (are left null) change this behavior. When I define a
@Tested(fullyInitialized=true)
field or parameter and some dependency is an interface and this interface isn't defined as@Injectable
then the Tested instance is null without any warning (at least there should be warning that there is missing@Tested
or@Injectable
for the interface). But I think that a better solution is let the dependency null.Example:
Now the
instantiateClassWithInterfaceInConstructor
fails because the parametercut
is null. If I want to work it I have to write the test like: