jmockit / jmockit1

Advanced Java library for integration testing, mocking, faking, and code coverage
Other
464 stars 240 forks source link

Support constructor injection when using Spring with @Qualifier #290

Closed tuegeb closed 8 years ago

tuegeb commented 8 years ago

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 commented 8 years ago

@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.

tuegeb commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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

tuegeb commented 8 years ago

Many thanks for the implementation of this great enhancement.