6

< TL;DR >

The problem: I'm using Cucumber-JVM and Spring for integration tests. An autowired class in the test steps class is not being created and is null.

< / TL;DR>

The tests work locally but fail on the build server, with a null pointer when a method call on the autowired bean is attempted.

Stack

  • Java 1.8 (Local and build server)
  • Maven 3.3.9 (Local and build server)
  • Local: Windows 8, Build Server: Ubuntu (can't fathom this makes a difference)

What I tried

The problem class is annotated @Component , I tried removing the @Component annotation and registering it in the spring context - this had no effect.

Setting the Spring log level to DEBUG showed very little and nothing of concern. For tests that use the Cucumber runner (@RunWith(Cucumber.class)), I see relatively little logs from Spring. Almost none compared to unrelated tests that use the SpringJunit4Runner.

I wrote a test that uses the SpringJunit4Runner instead of the Cucumber runner and autowires the problem class, it worked fine; the class was not null.

Code

POM

<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>
        <groupId>com.foo</groupId>
        <artifactId>matching-engine</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>core</artifactId>
    <name>core</name>
    <description>core matching engine</description>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.1.6.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.1.6.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>1.10.19</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.186</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java8</artifactId>
            <version>1.2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>1.2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>1.2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.13</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.13 </version>
        </dependency>
    </dependencies>

</project>

Cucumber Spring context (Cucumber.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <context:component-scan base-package="com.foo.matching" />
    <context:annotation-config/>
    <import resource="matching-engine-spring-context-TEST.xml" />

</beans>

Spring context used in tests (matching-engine-spring-context-TEST.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.1.xsd">
 <!-- Added this when I removed @Component from its class definition -->
    <bean id="testHelper" class="com.foo.matching.test.common.TestHelper"/>
    <bean id="orderTimeArrivalService" class="com.foo.matching.orderbook.MockOrderArrivalTimeService"/>
    <bean id="tradeExecutionService" class="com.foo.matching.execution.MockTradeExecutionService"/>
    <bean id="orderBookService" class="com.foo.matching.orderbook.TestOrderBookService"/>

</beans>

This doesn't work on the build server (fine locally):

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(plugin = {"pretty", "html:target/cucumber"})
public class MarketOrderTest {

}

public class MarketOrderSteps {

    @Autowired
    private TestHelper testHelper;

    @Given("^The order book looks like this before the trade is placed:$")
    public void setupOrderBook(List<LimitOrder> orders) {
        System.out.println("TestHelper: " + testHelper);
        testHelper.setupOrderBook(orders);
    }

This works fine on the build server and locally, leading me to believe the issue lies somewhere with Cucumber / the way I've configured it.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:*Cucumber.xml")
public class SpringTest{

    @Autowired
    private TestHelper testHelper;

    @Test
    public void test() {
        assertNotNull(testHelper);
    }
AfterWorkGuinness
  • 1,780
  • 4
  • 28
  • 47
  • After upgrading to cucumber to 1.2.4 (all cucumber info.cukes dependencies) it works. Still can't explain why it worked fine with 1.2.2 on windows and not on the build server. Also, after the upgrade, I see the expected amount of Spring logging at the debug level for tests run with the Cucumber runner. – AfterWorkGuinness Nov 26 '15 at 20:56
  • Forgot to mention: I needed to add @ContextConfiguration("classpath:*Cucumber.xml") to all my Step def classes for it to work after the upgrade. – AfterWorkGuinness Nov 26 '15 at 21:47

1 Answers1

8

I hate answering my own questions, but I've found the solution.

  1. Upgrade cucumber (all cucumber info.cukes dependencies) to 1.2.4
  2. Add @ContextConfiguration("classpath:*Cucumber.xml") to all my Step def classes

After the upgrade, I see the expected amount of Spring logging at the debug level for tests run with the Cucumber runner.

TuGordoBello
  • 4,350
  • 9
  • 52
  • 78
AfterWorkGuinness
  • 1,780
  • 4
  • 28
  • 47