5

For some reason, @Value annotations are not being injected into my class that I'm using as a base for other test classes. I'm new to Spring and my knowledge of Java isn't the best yet. I'm sure this will be much more obvious to someone with more experience, but I can't figure this out.

If I comment out the setDbProperties() method, the fields are populated, but with the @Value annotations alone, I get null values in the fields.

Here's my base test class:

package com.blah;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

import org.dbunit.DBTestCase;
import org.dbunit.PropertiesBasedJdbcDatabaseTester;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
        "classpath:/WEB-INF/application-context.xml",
        "classpath:/WEB-INF/application-context-test.xml" })
@ActiveProfiles("test")
public abstract class BaseDbTestCase extends DBTestCase {

    @Value("${test.db.connection.url}")
    private String connectionUrl;

    @Value("${test.db.driver.class}")
    private String driverClass;

    @Value("${test.db.username}")
    private String dbUserName;

    @Value("${test.db.password}")
    private String dbPassword;

    @Value("${test.db.datasource.path}")
    private String dataSource;

    public BaseDbTestCase(String name) throws IOException {

        super(name);

        // setDbProperties();

        System.setProperty(
                PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,
                driverClass);
        System.setProperty(
                PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL,
                connectionUrl);
        System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,
                dbUserName);
        System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,
                dbPassword);
    }

    @After
    public void after() throws Exception {
        DatabaseOperation.DELETE_ALL.execute(getConnection(), getDataSet());
    }

    @Override
    protected IDataSet getDataSet() throws Exception {
        return new FlatXmlDataSetBuilder()
                .build(new FileInputStream(dataSource));
    }

    private void setDbProperties() throws IOException, FileNotFoundException {

        Properties properties = new Properties();

        properties.load(new FileInputStream(
                "src/main/java/resources/application.properties"));

        connectionUrl = properties.getProperty("test.db.connection.url");
        driverClass = properties.getProperty("test.db.driver.class");
        dbUserName = properties.getProperty("test.db.username");
        dbPassword = properties.getProperty("test.db.password");
        dataSource = properties.getProperty("test.db.datasource.path");
    }

    @Override
    protected DatabaseOperation getSetUpOperation() throws Exception {
        return DatabaseOperation.REFRESH;
    }

    @Override
    protected DatabaseOperation getTearDownOperation() throws Exception {
        return DatabaseOperation.NONE;
    }

    @Before
    public void init() throws Exception {
        DatabaseOperation.REFRESH.execute(getConnection(), getDataSet());
    }

}

As you can see, other Spring (and non-Spring) annotations are working in this class.

I have <context:property-placeholder location="classpath:/resources/application.properties" /> in my application context.

Also, @Value annotations are working fine in classes that extend the BaseDbTestCase class.

TIA

Update: After taking @Biju's suggestion to move the setting of the system properties to the init() method (annotated with @Before), I'm now getting the following stack trace. Based on debugging, it doesn't seem to get past the constructor before throwing the error.

org.dbunit.assertion.DbAssertionFailedError: driverClass is null
    at org.dbunit.assertion.DefaultFailureHandler$DefaultFailureFactory.createFailure(DefaultFailureHandler.java:265)
    at org.dbunit.assertion.DefaultFailureHandler.createFailure(DefaultFailureHandler.java:110)
    at org.dbunit.assertion.SimpleAssert.fail(SimpleAssert.java:90)
    at org.dbunit.assertion.SimpleAssert.assertTrue(SimpleAssert.java:77)
    at org.dbunit.assertion.SimpleAssert.assertNotNullNorEmpty(SimpleAssert.java:61)
    at org.dbunit.JdbcDatabaseTester.<init>(JdbcDatabaseTester.java:103)
    at org.dbunit.PropertiesBasedJdbcDatabaseTester.<init>(PropertiesBasedJdbcDatabaseTester.java:68)
    at org.dbunit.DBTestCase.newDatabaseTester(DBTestCase.java:70)
    at org.dbunit.DatabaseTestCase.getDatabaseTester(DatabaseTestCase.java:109)
    at org.dbunit.DatabaseTestCase.setUp(DatabaseTestCase.java:151)
    at junit.framework.TestCase.runBare(TestCase.java:132)
    at junit.framework.TestResult$1.protect(TestResult.java:110)
    at junit.framework.TestResult.runProtected(TestResult.java:128)
    at junit.framework.TestResult.run(TestResult.java:113)
    at junit.framework.TestCase.run(TestCase.java:124)
    at junit.framework.TestSuite.runTest(TestSuite.java:243)
    at junit.framework.TestSuite.run(TestSuite.java:238)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Luke
  • 768
  • 2
  • 14
  • 33

2 Answers2

0

The reason I think is because when you derive a new test case based on your BaseDbTestCase the @ContextConfiguration in the BaseDbTestCase is not really visible in the derived test case - the @ContextConfiguration has to be on your final test case.

Update:

What I have put above is not correct, I tested once and it looks like the @ContextConfiguration and @RunWith even in the base class is sufficient and it correctly injects in the @Value fields in the base class.

Now the issue that you are having is because you are expecting the variables to be available in the constructor - they will not be, because the @Value is resolved AFTER the instance is created - so in your case since you are expecting it to be present in the constructor it will fail. You should instead be able to set the properties in @Before annotated method of Junit

Biju Kunjummen
  • 49,138
  • 14
  • 112
  • 125
  • It doesn't seem to matter where the `@ContextConfiguration` is. Either way, my fields annotated with `@Value` don't get populated. – Luke Oct 05 '12 at 18:47
  • Added an updated answer - the reason is you are expecting the value to be reflected in the constructor whereas `@Value` is resolved post construction. – Biju Kunjummen Oct 05 '12 at 19:47
  • For some reason, in this particular case, the setting of the system properties has to happen in the constructor, otherwise I never get past the constructor. Instead, I get the stack trace I updated my question with. Any ideas? – Luke Oct 05 '12 at 20:27
  • Can you please show your JdbcDatabaseTester class also, you may still be accessing some variable which has not been populated based on @Value in JdbcDatabaseTester – Biju Kunjummen Oct 05 '12 at 20:48
  • The `JdbcDatabaseTester` I'm using is the one that came with dbunit, as well as the `DBTestCase` class. See the `PropertiesBasedJdbcDatabaseTester` class here... http://www.dbunit.org/apidocs/org/dbunit/PropertiesBasedJdbcDatabaseTester.html – Luke Oct 05 '12 at 21:06
0

Have a concrete class which extends BaseDbTestCase and put @ContextConfiguration in the concrete class. Make your other test classes extend this concrete class.