New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support constructor injection when using Spring with @Qualifier #290

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

Comments

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

This comment has been minimized.

Show comment
Hide comment
@rliesenfeld

rliesenfeld Jun 13, 2016

Member

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

Member

rliesenfeld commented Jun 13, 2016

@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

This comment has been minimized.

Show comment
Hide comment
@tuegeb

tuegeb Jun 13, 2016

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;

tuegeb commented Jun 13, 2016

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

This comment has been minimized.

Show comment
Hide comment
@rliesenfeld

rliesenfeld Jun 13, 2016

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.

Member

rliesenfeld commented Jun 13, 2016

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

This comment has been minimized.

Show comment
Hide comment
@tuegeb

tuegeb Jun 13, 2016

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

tuegeb commented Jun 13, 2016

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

@tuegeb

This comment has been minimized.

Show comment
Hide comment
@tuegeb

tuegeb Jun 27, 2016

Many thanks for the implementation of this great enhancement.

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