JUnit + DbUnit: Switch database connection between development and testing environments
Asked Answered
C

3

6

I'm setting up some test scaffolding around an existing project. This includes some integration tests, using JUnit and DbUnit. I've also set up a Jenkins installation for continuous integration.

My problem involves changing the DB connections between the development and testing environments. I have my own product stack installed locally for quick ad-hoc testing and investigation. As I'm developing, I run tests against my private database, since it's faster and I won't ruin anybody else's day with buggy work-in-progress code.

Once code is checked in, Jenkins will run my tests. Right now it's still pointing at my local db. I'd prefer to have Jenkins run tests against a different database, one that is in the test environment.

Is there a best practice / strategy / technology / etc. for changing database connections for testing without having to change code? Bonus points if the solution allows Jenkins to run the same tests against multiple DBs (should be possible since DbUnit is agnostic).


Edits for more info:

The product is a large one, with dozens of different interacting components (usually in separate vms / processes). In a live system, the different processes typically communicate through the database. IE, the UI process writes changes to a table and the back-end process polls that table for changes. Yes, it's awful. For integration testing, I configure a system using the UI and capture that state with DbUnit. I can then run tests against that "input."

My component, and all new components, are managed by maven. DB Connections are currently hard-coded in the test setup. The DbUnit system works; I'd just like to be able to switch which database my tests are referencing depending on whether they're being run by me in my development environment, or run by Jenkins in the testing environment.

Charcuterie answered 22/5, 2012 at 16:21 Comment(0)
R
5

Assuming that you can externalise the database connectivity parameters for your tests into a .properties or .xml file, and that you are using Maven, then one approach is to use Maven properties (and optionally profiles) to define each environment, and use resource filtering to configure these properties into your configuration file.

For example, suppose during testing that you manage to externalise your database connectivity parameters into a file called datasource.properties in src/test/resources that looks like this:

datasource.driverClassName = ${test.datasource.driverClassName}
datasource.url = ${test.datasource.url}
datasource.username = ${test.datasource.username}
datasource.password = ${test.datasource.password}

And you have this in your POM:

<build>
  <testResources>
    <testResource>
      <directory>src/test/resources</directory>
      <filtering>true</filtering>
    </testResource>
  </testResources>
</build>

Then you could define test.datasource.driverClassName etc. properties in various ways to tell Maven to use different databases:

  • You could define defaults in the <properties> section of your POM;
  • You could define user-specific databases in the <properties> section of each developer's (and Jenkins') settings.xml;
  • You could define some profiles in your POM for different environments (e.g. development, jenkins, anotherDB) and run your build with different profiles.

If you go for the last option, then you could get Jenkins to run against multiple databases by performing multiple Maven builds from one Jenkins project, differing only by profile. That said, it would be better to have different Jenkins project for each environment.

Riannon answered 23/5, 2012 at 19:7 Comment(0)
I
1

Need to know more about your build tool. Are you using Maven? Where is your Database connection information stored? Are you using spring?

I do this currently, I use Maven with properties for my Main database connection and a seperate set of properties for my test database connection.

Since I use Spring, inside the maven test resources I have an applicationContext file that holds my datasource bean and references the test properties.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-    method="close">
    <property name="driverClassName" value="${test.jdbc.driverClassName}"/>
    <property name="url" value="${test.jdbc.url}"/>
    <property name="username" value="${test.jdbc.username}"/>
    <property name="password" value="${test.jdbc.password}"/>
    <property name="maxActive" value="100"/>
    <property name="maxWait" value="1000"/>
    <property name="poolPreparedStatements" value="true"/>
    <property name="defaultAutoCommit" value="true"/>
</bean>
Ilysa answered 22/5, 2012 at 16:34 Comment(2)
I'm embarrassingly noobly when it comes to Spring, but what little I know leads me to believe that it may be a helpful solution. I'm under the impression that you configured data sources in xml, and annotate your classes for runtime dependency injection. Would Spring be able to change which DB is injected based on the environment (dev vs. test)?Charcuterie
You are correct about configuration of the database connection in XML. But for what you are trying to accomplish I think you really should focus on what Maven is going to do for you and how Maven properties and profiles for your build can be utilized to customize your build and run your tests. Maven can run your unit tests and it has resources that are used just in the test phase then it can use another set of resources for your main build thats going to whatever environment. Jenkins runs maven builds, so you can simulate on your machine the exact same things Jenkins is going to be doing.Ilysa
I
1

A lot depends on your deployment environment. If you're deploying to a container (tomcat, jboss etc), then using a JNDI connection will isolate you from the DB environment. If you're building a stand alone java program, then you'll have to determine the environment on your own, and choose which db connection profile to use.

One way to do that is to have a separate database configuration per environment, and include that in your deployment. That way you don't have to complicate your code with worrying about what environment your in.

The other way is to pass the environment to your program and let your code figure out which configuration to use.

Industrial answered 22/5, 2012 at 16:38 Comment(2)
I've edited my post to include more info. I'm curious about the format of the "separate database configuration" you mention. Is that a .properties file (using ResourceBundle), or an XML (using some other tool), or something else entirely? I could put jdbc connection strings in a file and read that file, but that feels hackish. My assumption is that a tool / practice / format exists to do that in a more standard way. I just don't know what standard is.Charcuterie
.propreties files, or XML. I would use .properties files myself, I pretty much despise XML.Industrial

© 2022 - 2024 — McMap. All rights reserved.