Cuba 7 : overriding bean in integration tests

Hi

There is a difference between cuba 6 and cuba 7 regarding overriding of bean for testing.

In cuba 6 TestContainer inited this way

public TestContainer() {
[...]
        springConfig = "test-spring.xml";
[...]
}

So you could simple create a test-spring.xml in root package of core tests and you could easily override beans for testing.

Now in cuba 7:

    public TestContainer() {
[...]
        springConfig = "com/haulmont/cuba/testsupport/test-spring.xml";
[...]
}

I’m struggling to add my own test-spring.xml. I tried adding a test-app properties file along following ways

cuba.springContextConfig = +com/company/svp/test-spring.xml

=> does not work as it replace the entry cuba.springContextConfig = com/company/svp/spring.xml in app.properties

cuba.springContextConfig = com/company/svp/spring.xml com/company/svp/test-spring.xml

=> does not work as cuba spring is not taken into account

cuba.springContextConfig = +com/company/svp/spring.xml +com/company/svp/test-spring.xml

=> I had great expectations for this one but it fails

org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [+com/company/svp/test-spring.xml]; nested exception is java.io.FileNotFoundException: class path resource [+com/company/svp/test-spring.xml] cannot be opened because it does not exist

I would have liked to override the initAppContext() method of TestContainer to add location manually, but some stuff is private, namely

 protected void initAppContext() {
        EclipseLinkCustomizer.initTransientCompatibleAnnotations();

        String configProperty = AppContext.getProperty(AbstractAppContextLoader.SPRING_CONTEXT_CONFIG);

        StringTokenizer tokenizer = new StringTokenizer(configProperty);
        List<String> locations = tokenizer.getTokenList();
        locations.add(getSpringConfig());

        // here I would have like to add test-spring.xml location manually
        locations.add("test-spring.xml")

        // but springAppContext is private
        springAppContext = new CubaCoreApplicationContext(locations.toArray(new String[0]));
        AppContext.Internals.setApplicationContext(springAppContext);

        Events events = springAppContext.getBean(Events.class);
        events.publish(new AppContextInitializedEvent(springAppContext));
    }

Except if I’m mistaken this part is not documented, that could have helped.

Regards
Michael

Hi

I had to code an ugly workaround to overcome this situation, any help appreciated.

Regards
Michael

Hi,

This should work:

cuba.springContextConfig = +com/company/svp/spring.xml com/company/svp/test-spring.xml

Only one plus sign is required, in the beginning of the property value.

And it works, thanks @AlexBudarov !

Hi @AlexBudarov,

Following up on this topic, it works when I override one of my bean, but doesn’t if I override a CUBA bean.

Here is test-app.properties.

cuba.springContextConfig = +com/company/svp/spring.xml com/company/svp/testsupport/test-spring.xml 

Here is test-spring.xml

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean id="cuba_UserSessionSource" class="com.company.svp.testsupport.mocks.MyTestUserSessionSource"/>
    <bean id="svp_RemunerationService" class="com.company.svp.testsupport.mocks.RemunerationServiceTestBean"/>

</beans>

Overriding of svp_RemunerationService does work, but not overriding of cuba_UserSessionSource.

com.haulmont.cuba.testsupport.TestUserSessionSource cannot be cast to com.company.svp.testsupport.mocks.MyTestUserSessionSource
java.lang.ClassCastException: com.haulmont.cuba.testsupport.TestUserSessionSource cannot be cast to com.company.svp.testsupport.mocks.MyTestUserSessionSource

Regards,
Michaël

With a sample project to replicate the issue.

expected:<class com.company.testbeanover.testsupport.MyTestUserSessionSource> but was:<class com.haulmont.cuba.testsupport.TestUserSessionSource>

testbeanover.zip (72.6 KB)

Hi,

As you can guess by class name, CUBA test framework itself replaces the cuba_UserSessionSource bean definition by the mock class.

I think you will be able to extend the bean if you make your own test class the descendant of the com.haulmont.cuba.testsupport.TestUserSessionSource class and preserve invariants used by the cuba-core test framework (not tested personally).

The bean implementation class or its ascendant does not matter. I should be able to replace any bean even a CUBA’s one.

Done that many times in a 6.10 project without any trouble, look below. Just looking for how to do in CUBA 7.

    <bean id="cuba_UserSessionSource" class="com.busy.app.testsupport.MyTestUserSessionSource"/>
    <bean id="busy_DataSecFilter" class="com.busy.app.testsupport.TestDataSecFilter"/>
    <bean id="cuba_EmailSender" class="com.busy.app.testsupport.MyTestEmailSender"/>
    <bean id="cuba_MailSender" class="com.busy.app.testsupport.MyTestMailSender"/>
    <!--<bean id="cuba_Emailer" class="com.haulmont.cuba.testsupport.TestEmailer"/>-->
    <bean id="cuba_Emailer" class="com.busy.app.testsupport.MyTestEmailerAPI">
    </bean>

I’ve tried myself in my project, and yes, it’s not simple.

The complexity is that cuba’s com.haulmont.cuba.testsupport.TestContainer has its own field springConfig which is initialized with “com/haulmont/cuba/testsupport/test-spring.xml” value. This spring config is always appended as the last spring file in the chain.
This file has its own definition for the UserSessionSource:

 <bean id="cuba_UserSessionSource" class="com.haulmont.cuba.testsupport.TestUserSessionSource"/>

I have managed to make things working by doing the following in my project (project name is playground):

  1. In my test-app.properties changed the springContextConfig line to the following:
cuba.springContextConfig = +com/company/playground/spring.xml com/haulmont/cuba/testsupport/test-spring.xml
  1. In com.company.playground.PlaygroundTestContainer#PlaygroundTestContainer constructor I put this line:
    public PlaygroundTestContainer() {
        super();
        springConfig = "com/company/playground/test-spring.xml";
...
  1. In my playground/test-spring.xml I used override bean definition:
<bean id="cuba_UserSessionSource" class="com.company.playground.core.MyUserSessionSource"/>

Then spring context file chain is formed as it should be:

  • cuba
  • app components …
  • com/company/playground/spring.xml
  • com/haulmont/cuba/testsupport/test-spring.xml from CUBA tests framework
  • com/company/playground/test-spring.xml from my tests

and MyUserSessionSource gets instantiated

You nailed it @AlexBudarov, thanks, it works great.

Maybe at some point this kind of configuration could be simplified in cuba 7.

Spring configuration for CUBA tests was quite transparent in 6.10.

Regards
Michael