Pull request Compare This branch is 2 commits ahead, 3644 commits behind hibernate:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Hibernate Matrix Testing


Run hibernate-core functional tests on other DBs besides default H2 easily.

Well, although Functional Testing and Unit Testing are already well known in the developer world, but we ( hibernate team ) use a little different definition:

Unit Test

Test doesn't need DB involved or only the default DB (currently is H2) is fine, then we call it is a unit test.

Functional Test

Test which is used to verify a hibernate function and needs to make sure this function works fine on all DBs.

Just to be clear, in hibernate codebase, most tests we have are functional tests by this definition.

And all hibernate functional tests are also unit tests, since they are also supposed to pass on the default DB (H2).


Since Hibernate Core has moved to Gradle from Maven, so we created a gradle plugin, called MatrixTestingPlugin, to run hibernate functional tests on the DB matrix (this is why it is called MatrixTestingPlugin :).

The source of this plugin is here, this is used by Hibernate Core only right now, so this post is specific to hibernate core project only, hope one day we could denote it to the gradle community.

How to use this plugin

  1. apply this plugin in your gradle build script (of course!)

    In hibernate-core/hibernate-core.gradle we have this line:

    apply plugin: org.hibernate.gradle.testing.matrix.MatrixTestingPlugin

  2. SourceSet separation

    Although it is possible to define a logical separation in Gradle (see this), I would like physical separation of unit tests and functional tests, so, we have this project structure:

    localhost:hibernate-core stliu$ tree -L 3
    ├── hibernate-core.gradle
    ├── src
        ├── main
        │   ├── antlr
        │   ├── java
        │   ├── javadoc
        │   ├── resources
        │   └── xjb
        ├── matrix
        │   └── java
        └── test
            ├── java
            └── resources
    • matrix/java

      all functional tests go into this directory

    • test/java

      all unit tests go into this directory

    • test/resources

      all resources for functional tests and unit tests, yes, resource files in this directory are shared for both, so you don't need to copy one file to both place, for example, log4j.properties.

    To make idea plugin (and eclipse plugin) works, we also have this defined in hibernate-core.gradle:

    sourceSets {
        matrix {
            java {
                srcDir 'src/matrix/java'
            resources {
                srcDir 'src/matrix/resources'
    ideaModule {
        sourceDirs += file( '$buildDir/generated-src/antlr/main' )
        testSourceDirs += file( 'src/matrix/java')
        testSourceDirs += file( 'src/matrix/resources')
  3. DB profile

    A DB profile defines the JDBC driver and DB connection info ( and hibernate properties for this DB ) that hibernate should use to run functional tests on this DB.

    A DB profile looks like this:

    ├── mysql50
    │   ├── jdbc
    │   │   └── mysql-connector-java-5.1.9.jar
    │   ├── matrix.gradle
    │   └── resources
    │       └── hibernate.properties

    There are two ways to define JDBC driver, as showed above, put the driver jar file into jdbc directory, or use matrix.gradle file, below is something you should put into your matrix.gradle file:

    jdbcDependency "mysql:mysql-connector-java:5.1.17"

    As you can see, just add the driver's GAV into jdbcDependency configuration, then MatrixTestingPlugin will look it up from maven repository defined in the build.gradle and add it to the testCompile scope.

    For DB connection info, you should add it into resources/hibernate.properties (if you have Redhat VPN access, you can use the DB maintained by JBoss QA and get the DB connection automatically through DB Allocator, see below).

    NOTE: this hibernate.properties will overrides the one in hibernate-core/test/resources/hibernate.properties if same property name defined in both place.

    And, the DB profile name is the directory name, in this example, it is mysql50.

    The default DB profile location is in hibernate-core/databases directory, but you can also reallocate it to another place by using system property hibernate-matrix-databases, see below for more details.

Matrix Tasks

Once you have DB profiles defined, you could run gradle tasks --all, this will list all tasks you can use.

For example, there will be a matrix task, which depends on all of other matrix_${profile_name} tasks, each DB profile has a matrix_${profile_name} task, so if you want to run hibernate functional tests on all DB profiles, then just call gradle matrix or if you want to run them on mysql only, then gradle mysql50.

In this case ( run gradle matrix or its sub-task gradle matrix_${profile_name} ), only tests in src/matrix/java will be ran, and hibernate.properties come from your db profile/resources/hibernate.properties (and test/resources/hibernate.properties).

Matrix test results are in target/matrix/${profile_name}/test-results.

But, there is also a test task, this task runs all tests, both src/matrix/java and src/test/java, with src/test/resources on default DB (as above said, all functional tests are also unit tests).

Unit test results are in target/test-results, so if you run test task, all test results are in here, instead of target/matrix/${profile_name}/test-results, just as normal.

Configuration Properties

There are two way to pass a system property to gradle build:

1. The original java way, -Dxxx=yyy
2. Add it to ${user.home}/.gradle/gradle.properties with a 'systemProp' prefix, like 'systemProp.xxx=yyy' 
  • hibernate-matrix-databases

    This property is used to define the location of DB profile container.
    Accept value: absolute path of DB profile container directory.

  • hibernate-matrix-ignore

    This property is used to ignore some DB profiles (or all of them), so if you run matrix, the ignored profile matrix task won't be run.

    Accept value : all or ${profile name1},${profile name2},${profile name3}

DB Allocator (JBoss internally, VPN required)

For users who has access to JBoss QA lab (need Redhat VPN), here is a better way to run matrix tests, you don't need to have a DB instance on your side, but you can use DB instance in JBoss QA Lab.

And the connection info can be queried from DB Allocator automatically.

This feature is disabled by default, to enable it, you need this system property

  • hibernate-matrix-dballcoation

    Accept value: all or ${profile name1},${profile name2},${profile name3}

For example, if you want to run matrix test on postgresql84, you can use this command

    ./gradlew clean test matrix_postgresql84 -Dhibernate-matrix-dballocation=postgresql84

what does this command do actually?
1. test
    run 'src/test/java' on default H2, test results in 'target/test-results'
    run 'src/matrix/java' on default H2, test results in 'target/test-results'

2. query postgresql 84 db instance connection info
3. run 'src/matrix/java' on postgresql 84 with 'databases/postgresql84/matrix.gradle' defined jdbc driver and 'databases/postgresql84/resources/hibernate.properties' and postgresql84 db instance connection info (this info will override those defined in hibernate.properties), test results in 'target/matrix/postgresql84/results'

Some DBs need we tweak url with some configurations after get it from DB allocator, so, we can use this system property:

  • hibernate-matrix-dballocation-url-postfix-${dbname}

    for example:

    `-Dhibernate-matrix-dballocation-url-postfix-sybase155="?SQLINITSTRING=set quoted_identifier on&DYNAMIC_PREPARE=true"`
  • hibernate-matrix-dballocation-requestee This property is used to define the DB Allocator requester name, default is hibernate