Eugene Beyer
Eugene Beyer

Reputation: 53

Layering or overriding ActiveProfiles within the Spring Framework

Is there a way to override an @ActiveProfile set by a test superclass within Spring?

Here's my configuration:

<beans profile="integration">
    <bean class="org.easymock.EasyMock" factory-method="createMock">
        <constructor-arg value="com.mycompany.MyInterface" />
    </bean>
</beans>

<beans profile="production,one-off-test">
    <bean class="com.mycompany.MyInterfaceImpl" />
</beans>

The superclass to all the tests looks like this:

@ActiveProfiles("integration")
public abstract class TestBase {

And in my new test class I would like to do this:

@ActiveProfiles("one-off-test")
public class MyTest extends TestBase {

Not inheriting from TestBase isn't really an option. When I try to run this, the error I get is:

No qualifying bean of type [com.mycompany.MyInterface] is defined: expected single matching bean but found 2
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.mycompany.MyInterface] is defined: expected single matching bean but found 2: org.easymock.EasyMock#1,com.mycompany.MyInterfaceImpl#0
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:970)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
    ... 46 more

What would be even better is to be able to layer the profiles, so that if a bean exists for profile one-off-test inject that, otherwise inject the integration profile bean.

Any help would be highly appreciated.

Upvotes: 5

Views: 3421

Answers (2)

geoand
geoand

Reputation: 64011

One solution to the problem you presented is to use primary like this:

<beans profile="integration">
    <bean class="org.easymock.EasyMock" factory-method="createMock" primary=true>
        <constructor-arg value="com.mycompany.MyInterface" />
    </bean>
</beans>

You don't need to change anything else because Spring will use the bean from profile integration wherever a bean of type MyInterface. Spring does that despite the fact that there are multiple beans of that type present.

Check out this which gives a few more details on how primary works.

Upvotes: 0

tmarwen
tmarwen

Reputation: 16364

You may need to specify the active profiles through System Properties:

-Dspring.profiles.active="integration"

in case you want to use the related bean implementation or

-Dspring.profiles.active="one-off-test"

to use the one-off-test profile beans.

Then you will have to set the inheritProfiles annotation propertyto false which will prevent the current annotated test case to discart subclass profiles:

@ActiveProfiles(profiles = {"one-off-test"}, inheritProfiles= false)
public class MyTest extends TestBase {}

Upvotes: 10

Related Questions