4

I'm setting up a CI situation in which I will deploy my web app to a test environment. In this test environment, I want the business objects used by the app to be mocks of the real ones; the mocks will return static test data. I'm using this to run tests agains my ui. I'm controlling the injections of these business object dependencies with Spring; it's a struts 2 application, for what that's worth.

My question is Maven related, I think. What is the best way to have my Maven build determine whether or not to build the spring configuration out for injecting the mocks or injecting the real thing? Is this a good use for maven profiles? Other alternatives?

chad
  • 7,369
  • 6
  • 37
  • 56
  • 1
    Using profiles can be a good choice. You could have some property, say, `spring.context` and change its value to point either to production context file, or to test context file. – Andrew Logvinov Oct 26 '12 at 19:43
  • wouldn't that lead to two different versions of my war artifact in the maven repos? one where it's built with the mock data for the test server deployment, and one with the real, live system settings? – chad Oct 26 '12 at 21:32

2 Answers2

3

Spring itself has support for profiles (if you're using 3.1 or newer), for a web-application you can use context-parameter to set the active profile for different environments in the web.xml:

<context-param>
   <param-name>spring.profiles.default</param-name>
   <param-value>test</param-value>
</context-param>

Edit: For Maven & Jenkins, you should be able to set the parameter for a build job as follows:

First, let Maven filter your xml-resources (in this example, only files ending with xml are filtered, others are included without filtering) by adding the following into your pom.xml inside the <build> </build> -tags:

    <resources>
        <resource>
            <directory>src/main/webapp</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/webapp</directory>
            <filtering>false</filtering>
            <excludes>
                <exclude>**/*xml</exclude>
            </excludes>
        </resource>
    </resources> 

Then, parameterize the context-param in your web.xml:

<context-param>
   <param-name>spring.profiles.default</param-name>
   <param-value>${env.SPRINGPROFILE}</param-value>
</context-param>

Then parameterize the build job in Jenkins to set the desired string parameter for SPRINGPROFILE (for example test or prod): https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Build

esaj
  • 15,875
  • 5
  • 38
  • 52
  • But this still requires build time logic to decide how to configure the values in the web.xml, correct? – chad Oct 26 '12 at 20:12
  • @chad: True, see my edited answer. I haven't actually tried this out, as I don't have access to Jenkins from home, but I believe it should work as is. – esaj Oct 26 '12 at 20:32
  • I think maybe my problem is more complex. I believe you have given a good way to achieve one solution, but here's the problem I see. If my test build produces a war that has different configuration inside of it, then that will get deployed to maven. But this test data setting is NOT what my team members expect when they pull down that same snapshot dependency for their dev builds; they want it to work off of normal business logic. I think maybe my answer is more complicated, requiring me to externalize my spring config. – chad Oct 26 '12 at 21:06
  • Maybe I can use the Spring profiles and drive them from outside my webapp. . . this question ( http://stackoverflow.com/questions/2424118/maven-best-practice-for-generating-artifacts-for-multiple-environments-prod-te ) is where I got this idea about externalizing the config. – chad Oct 26 '12 at 21:08
1

It's probably a bad idea to do anything with the build of the web app artifact ( Maven best practice for generating artifacts for multiple environments [prod, test, dev] with CI/Hudson support? ). While you could use various mechanisms to produce a WAR file with different configurations of the Spring injections for different contexts, the WAR artifact should be the same every time it's built.

In order to extract the configuration out of the WAR, I have used Spring 3's ability to pull in override values from an external property file. I define default, i.e. produciton, values of my business objects. And I configure spring to check for the existence of a properties file, something I will deploy when the app is in a testing environment and requires mock injections. If that properties file exists, it's values are injected instead. Here's the relevent bit of the spring config file.

<!-- These are the default values -->
    <util:properties id="defaultBeanClasses">
    <prop key="myManagerA">com.myco.ManagerAImpl</prop>
    <prop key="myManagerB">com.myco.ManagerBImpl</prop>
</util:properties>

<!-- Pull in the mock overrides if they exist. -->
<context:property-placeholder 
    location="file:///my/location/mockBeans.properties"
    ignore-resource-not-found="true"
    properties-ref="defaultBeanClasses"/>

<!-- The beans themselves. -->  
<bean id="managerA" class="${myManagerA}"/>
<bean id="managerB" class="${myManagerB}"/>

And here is the contents of the external "mockBeans.properties" file:

#Define mock implementations for core managers
myManagerA=com.myco.ManagerAMockImpl
myManagerB=com.myco.ManagerBMockImpl

This works nicely. You can even include the mockBeans.properties file in the actual WAR, if you like, but not in the live location. Then the test environment task would be too move it to the location pointed at by the spring config. Alternatively, you could have the mock properties reside in a completely different project.

Community
  • 1
  • 1
chad
  • 7,369
  • 6
  • 37
  • 56