Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

As a follow-up to Cucumber Springframework Integration, I needed to get more flexibility to control when Cucumber was ran. With the current configuration, the Cucumber tests ran every time the Maven build ran. This is as designed, but we needed more flexibility. We also needed the ability to run the Cucumber tests, but without building the code again. I wanted to build the code once, then tests it as it was deployed to each environment. I wanted to ensure that the golden build was the only build that I was using. This cuts down on the variables when there are problem. If this code works in DEV, but doesn’t work in the next environment, then it is something different in the environments.

Controlling database connections per environment

We had the problem where we thought that the Cucumber tests were being ran against our integration environment. This didn’t turn out to be true, because the Cucumber tests were using the same datasource as our Junit tests. Those always ran against our development environment. To overcome the datasource issue, I moved from a single datasource to a datasource per environment and using Spring profiles to determine which database connection to use. As example of what I placed in the Springframework configuration is:

    <beans profile="default">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://dev_dns/my_database?zeroDateTimeBehavior=convertToNull"
              p:username="_username_"
              p:password="_password_"
              p:maxWait="3000"
              p:maxIdle="100"
              p:maxActive="10"/>
    </beans>
    <beans profile="dev">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://dev_dns/my_database?zeroDateTimeBehavior=convertToNull"
              p:username="_username_"
              p:password="_password_"
              p:maxWait="3000"
              p:maxIdle="100"
              p:maxActive="10"/>
    </beans>
    <beans profile="ite">
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
              p:driverClassName="com.mysql.jdbc.Driver"
              p:url="jdbc:mysql://ite_dns/my_database?zeroDateTimeBehavior=convertToNull"
              p:username="_username_"
              p:password="_password_"
              p:maxWait="3000"
              p:maxIdle="100"
              p:maxActive="10"/>
    </beans>

Activating a Spring profile

Now that you have multiple Spring profiles, you can control which profile is active by passing the following to the JVM running the program. If you don’t specify a profile, then default is assumed.

-Dspring.profiles.active=dev

Moving the Cucumber to it’s own Maven module

Once I had that, I realized that having the Cucumber tests as part of the code module caused a problem. Maven processes the build lifecycle steps in sequence. If I wanted to run the tests, then the code would have to be built. This was exactly what I did not want. To overcome this, I needed to move the Cucumber to it’s own module, then if I ran the tests via Maven, there wouldn’t be any code to compile, because the code would be in another module.

I started with a module named bonus-core. This had all of the business logic and the Junit and Cucumber that was used to tests the business logic. I moved all of the Cucumber specific dependencies, steps and features to a new module named bonus-core-integration. This is what the new module’s pom.xml contained:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>bonus</artifactId>
        <groupId>com.cdi.igs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>bonus-core-integration</artifactId>

    <dependencies>
        <!-- The module containing the code to be tested with Cucumber -->
        <dependency>
            <groupId>com.cdi.igs</groupId>
            <artifactId>bonus-core</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--Cucumber related-->
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-jvm-deps</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Once this was done, I could run the bonus-code module and execute the Junit tests, but without the Cucumber tests. Since the Cucumber was not in bonus-code-integration, I could now run just the Cucumber tests, but without building.

Parent pom.xml with and without integration tests

This was closer to what I wanted, but it means that If I run mvn test from the parent pom.xml, the Cucumber tests were always ran. I wanted the flexibility to run the main build without the Cucumber tests. I wanted this because we had a previous project that had so many Cucumber tests, that they took 20 minutes to run. We didn’t have this problem in this project, but I didn’t want to have to scramble if that situation developed. I started with the following modules in the parent pom.xml:

<modules>
    <module>bonus-models</module>
    <module>bonus-core</module>
    <module>bonus-services</module>
    <module>bonus-core-integration</module>
</modules>

I wanted the ability to turn off the execution of the integration modules when needed. I came up with the following:

    <profiles>
        <profile>
            <id>bonus-all</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <modules>
                <module>bonus-models</module>
                <module>bonus-core</module>
                <module>bonus-services</module>
                <module>bonus-core-integration</module>
            </modules>
        </profile>
        <profile>
            <id>bonus-no-integration</id>
            <modules>
                <module>bonus-models</module>
                <module>bonus-core</module>
                <module>bonus-services</module>
            </modules>
        </profile>
    </profiles>

This means that if the developer doesn’t do anything, then they will get all of the modules. They can specify the alternative profile to exclude to the integration tests for this project. I named them _project_-all and _project_-no-integration because we also had the case there there was a master pom for this project. If the project was opened from that view, I wanted the ability to turn off the integration for individual projects, not just all of them across all of the projects under the master pom.xml.

The end result

Once all of my changes were completed, I was able to control all of the test execution according to whatever situation I was in at the time.

  • All tests – I could run the Junit tests and the integration tests simply by running mvn clean test from the parent pom.xml
  • Only Junit tests – I could run the Junit tests and skip all the integration tests by running mvn clean test -Pbonus-no-integration from the parent pom.xml
  • Only Integration tests – I could run the integration tests and skip the build and the Junit tests by running mvn clean test from within the bonus-core-integration module instead of the parent module. If this is used, then the code would be retrieved from our local Artifactory.
LinkedIn Auto Publish Powered By : XYZScripts.com