Support constructor injection when using Spring with @Qualifier #290

Closed
tuegeb opened this Issue Jun 11, 2016 · 5 comments

Projects

None yet

2 participants

@tuegeb
tuegeb commented Jun 11, 2016
  • Version of JMockit that was used: 1.24
  • Description of the problem or enhancement request:
    Support constructor injection when using Spring with @Qualifier. Real (unmocked) instance of the type which corresponds to the chosen qualifier, should satisfy the constructor of the tested class.

Currently this test fails:

public class ClassWithQualifiedSpringDependencyTest {
    @Tested(fullyInitialized = true)
    ClassWithQualifiedDependency tested;

    @BeforeClass
    public static void prepare() {
        Dependency.class.getName();
    }

    @Test
    public void testInjection() {
        assertNotNull(tested);
        assertNotNull(tested.getDependency());
        assertSame(tested.getDependency().getClass(), Dependency.class);
    }

    @Service
    public class ClassWithQualifiedDependency {
        private IDependency dependency;

        @Autowired
        public ClassWithQualifiedDependency(@Qualifier("commonDependency") IDependency d) {
            this.dependency = d;
        }

        public IDependency getDependency() {
            return dependency;
        }
    }

    public interface IDependency {
    }

    @Service("commonDependency")
    public class Dependency implements IDependency {
    }

    @Service("lessCommonDependency")
    public class Dependency2 implements IDependency {
    }
}

The following exception is thrown:

java.lang.IllegalArgumentException: No constructor in tested class that can be satisfied by available injectables
public ClassWithQualifiedSpringDependencyTest$ClassWithQualitfiedDependency(ClassWithQualifiedSpringDependencyTest,ClassWithQualifiedSpringDependencyTest$IDependency)
disregarded because parameter names are not available
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

@rliesenfeld
Member
rliesenfeld commented Jun 13, 2016 edited

@Qualifier is already supported, but Spring's @Service/@Repository/@Component/@Controller/@Primary is not.

However, I believe a better solution will be to improve the support for @Tested object reuse. With that, the test class would be written as:

public class ClassWithQualifiedSpringDependencyTest {
    @Tested(fullyInitialized = true) Dependency commonDependency;
    @Tested(fullyInitialized = true) ClassWithQualifiedDependency tested;

    @Test
    public void testInjection() {
        assertNotNull(tested);
        assertNotNull(tested.getDependency());
        assertSame(tested.getDependency().getClass(), Dependency.class);
    }
}

... where the "commonDependency" tested object would be reused for the dependency in the other tested object. This already works, provided other classes implementing the interface haven't been loaded yet.

@rliesenfeld rliesenfeld self-assigned this Jun 13, 2016
@tuegeb
tuegeb commented Jun 13, 2016 edited

Thanks, that sounds good. It is not always possible to control which implementation classes will be loaded before the test execution: It would be helpful, if the preferred implementation class could optionally be set via an attribute in the annotation. Example:

@Tested(fullyInitialized = true, 
        preferredClass = Dependency.class)
IDependency commonDependency;
@Tested(fullyInitialized = true)
ClassWithQualitfiedDependency tested;
@rliesenfeld
Member

You will be able to use the implementation class (Dependency) as the type for the @Tested field, so this "preferredClass" attribute won't be necessary.

@tuegeb
tuegeb commented Jun 13, 2016

That is even better, as it is much more intuitive.

@rliesenfeld rliesenfeld added a commit that closed this issue Jun 19, 2016
@rliesenfeld rliesenfeld @Tested(fullyInitialized): added support for the reuse of @Tested obj…
…ects whose declared type is a subtype of the target injection point type; closes #290.
badb34f
@tuegeb
tuegeb commented Jun 27, 2016

Many thanks for the implementation of this great enhancement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment