6

In my unit tests I autowired some DataSources, which use URLs like

jdbc:derby:memory:mydb;create=true

to create an in-memory DBs.

To drop an in-memory Derby db you have to connect with:

jdbc:derby:memory:mydb;drop=true

I would like this to happen after every test and start with a fresh db. How can I do this using Spring?

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
Puce
  • 37,247
  • 13
  • 80
  • 152
  • where did you get this param from `drop=true` I don't find it derby documentation. Adding it causes `org.apache.derby.iapi.error.StandardException: Conflicting create attributes specified.` – Anand Rockzz Nov 05 '18 at 03:13
  • @AnandRockzz it's a long time since I used the Derby db, but a quick search showed: https://db.apache.org/derby/docs/10.14/devguide/cdevdvlpinmemdb.html – Puce Nov 05 '18 at 19:44

6 Answers6

5

How to shutdown Derby in-memory database Properly

gave me a hint to a solution:

    mydb.drop.url = jdbc:derby:memory:mydb;drop=true

    ...

    <bean id="mydbDropUrl" class="java.lang.String">
    <constructor-arg value="${mydb.drop.url}" />
</bean>

    ...

    @Resource
private String mydbDropUrl;        

    @After
public void tearDown() {
    try {
        DriverManager.getConnection(mydbDropUrl);
    } catch (SQLException e) {
        // ignore
    }
}

A downside is the use of the String constructor which accepts a String (an immutable String object around an immutable String object). I read that there is a @Value annotation in Spring 3, which might help here, but I'm using Spring 2.5.

Please let me know if you have a nicer solution.

Community
  • 1
  • 1
Puce
  • 37,247
  • 13
  • 80
  • 152
  • Following http://docs.oracle.com/javadb/10.8.1.2/getstart/rwwdactivity3.html as an example, rather than simply catching and discarding the `SQLException` it might be better to discard it only if `e.getSQLState().equals("08006")` – Raedwald Mar 11 '12 at 15:21
4

There is a database-agnostic way to do this if you are using Spring together with Hibernate.

Make sure the application context will be created / destroyed before / after every test method:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:application-context-test.xml"})
@TestExecutionListeners({DirtiesContextTestExecutionListener.class, 
    DependencyInjectionTestExecutionListener.class})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public abstract class AbstractTest {

}

Instruct Hibernate to auto create the schema on startup and to drop the schema on shutdown:

hibernate.hbm2ddl.auto = create-drop

Now before every test

  • the application context is created and the required spring beans are injected (spring)
  • the database structures are created (hibernate)
  • the import.sql is executed if present (hibernate)

and after every test

  • the application context is destroyed (spring)
  • the database schema is dropped (hibernate).

If you are using transactions, you may want to add the TransactionalTestExecutionListener.

tekbe
  • 49
  • 1
  • 3
2

After spring test 3, you can use annotations to inject configurations:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-test.xml")
public class MyTest {
}
Jay
  • 326
  • 2
  • 8
1

If you use the spring-test.jar library, you can do something like this:

public class MyDataSourceSpringTest extends
AbstractTransactionalDataSourceSpringContextTests {

    @Override
    protected String[] getConfigLocations() {
        return new String[]{"classpath:test-context.xml"};
    }

    @Override
    protected void onSetUpInTransaction() throws Exception {
        super.deleteFromTables(new String[]{"myTable"});
        super.executeSqlScript("file:db/load_data.sql", true);
    }
}

And an updated version based on latest comment, that drops db and recreates tables before every test:

public class MyDataSourceSpringTest extends
    AbstractTransactionalDataSourceSpringContextTests {

        @Override
        protected String[] getConfigLocations() {
            return new String[]{"classpath:test-context.xml"};
        }

        @Override
        protected void onSetUpInTransaction() throws Exception {
            super.executeSqlScript("file:db/recreate_tables.sql", true);
        }
}
Maria Ioannidou
  • 1,544
  • 1
  • 15
  • 36
  • I don't want to delete data from tables or even to drop tables. I want to drop the whole db an recreate it for every test, which is safer IMHO. – Puce Jan 18 '11 at 10:47
  • You could create a sql script that drops the db and recreates the tables and execute it as shown above. I'll update my answer appropriately. – Maria Ioannidou Jan 18 '11 at 11:15
  • I'm not sure this will work, since "jdbc:derby:memory:mydb;drop=true" is a URL and not an SQL statement. – Puce Jan 18 '11 at 11:35
1

Just do something like:

public class DatabaseTest implements ApplicationContextAware {
    private ApplicationContext context;
    private DataSource source;

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.context = applicationContext;
    }

    @Before
    public void before() {
        source = (DataSource) dataSource.getBean("dataSource", DataSource.class);
    }

    @After
    public void after() {
        source = null;
    }
}

Make your bean have a scope of prototype (scope="prototype"). This will get a new instance of the data source before every test.

hisdrewness
  • 7,599
  • 2
  • 21
  • 28
  • That's clever (+1), but not very practical (it means you have to manually wire all beans that use the datasource) – Sean Patrick Floyd Jan 18 '11 at 08:40
  • I have doubts, if this approach really works: Are you saying that context.getBean() will return a new instance rather than the same instance? I don't think this is the case, which means you will gain nothing. There is a @DirtiesContext annotation, but even that would not open a connection with "jdbc:derby:memory:mydb;drop=true" to delete the in-memory db. – Puce Jan 18 '11 at 10:43
0

This is what we do at the start of every test.

  1. Drop all Previous Objects.

  2. Create all tables mentioned in the create_table.sql

  3. Insert values onto the created tables based on what you want to test.

      @Before
      public void initialInMemoryDatabase() throws IOException, FileNotFoundException {
    
      inMemoryDerbyDatabase.dropAllObjects();
      inMemoryDerbyDatabase.executeSqlFile("/create_table_policy_version_manager.sql");
      inMemoryDerbyDatabase.executeSqlFile("/insert_table_policy_version_manager.sql");
    
      }
    

Works like a charm!

Mogsdad
  • 44,709
  • 21
  • 151
  • 275