|
| 1 | +##Hibernate Matrix Testing |
| 2 | + |
| 3 | +#### Goal |
| 4 | + |
| 5 | +Run hibernate-core _functional_ tests on other DBs besides default H2 easily. |
| 6 | + |
| 7 | +Well, although [Functional Testing](http://en.wikipedia.org/wiki/Functional_testing) and [Unit Testing](http://en.wikipedia.org/wiki/Unit_testing) are already well known in the developer world, but we ( hibernate team ) use a little different definition: |
| 8 | + |
| 9 | +###### Unit Test |
| 10 | + |
| 11 | +Test doesn't need DB involved or only the default DB (currently is [H2](http://www.h2database.com/)) is fine, then we call it is a _unit test_. |
| 12 | + |
| 13 | +###### Functional Test |
| 14 | + |
| 15 | +Test which is used to verify a hibernate function and needs to make sure this function works fine on all DBs. |
| 16 | + |
| 17 | +Just to be clear, in hibernate codebase, most tests we have are _functional tests_ by this definition. |
| 18 | + |
| 19 | +And all hibernate _functional tests_ are also _unit tests_, since they are also supposed to pass on the default DB (H2). |
| 20 | + |
| 21 | +#### MatrixTestingPlugin |
| 22 | + |
| 23 | +Since Hibernate Core has moved to [Gradle](http://www.gradle.org/) from [Maven](http://maven.apache.org/), so we created a gradle plugin, called _MatrixTestingPlugin_, to run hibernate functional tests on the DB matrix (this is why it is called _MatrixTestingPlugin_ :). |
| 24 | + |
| 25 | +The source of this plugin is [here](https://github.com/hibernate/hibernate-core/tree/master/buildSrc), 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. |
| 26 | + |
| 27 | +#### How to use this plugin |
| 28 | + |
| 29 | +1. apply this plugin in your gradle build script (of course!) |
| 30 | + |
| 31 | + In [hibernate-core/hibernate-core.gradle](https://github.com/hibernate/hibernate-core/blob/master/hibernate-core/hibernate-core.gradle) we have this line: |
| 32 | + |
| 33 | + `apply plugin: org.hibernate.gradle.testing.matrix.MatrixTestingPlugin` |
| 34 | + |
| 35 | +2. SourceSet separation |
| 36 | + |
| 37 | + Although it is possible to define a logical separation in Gradle (see [this](http://gradle.org/current/docs/javadoc/org/gradle/api/tasks/SourceSet.html)), I would like physical separation of unit tests and functional tests, so, we have this project structure: |
| 38 | + |
| 39 | + localhost:hibernate-core stliu$ tree -L 3 |
| 40 | + ├── hibernate-core.gradle |
| 41 | + ├── src |
| 42 | + ├── main |
| 43 | + │ ├── antlr |
| 44 | + │ ├── java |
| 45 | + │ ├── javadoc |
| 46 | + │ ├── resources |
| 47 | + │ └── xjb |
| 48 | + ├── matrix |
| 49 | + │ └── java |
| 50 | + └── test |
| 51 | + ├── java |
| 52 | + └── resources |
| 53 | + |
| 54 | + * matrix/java |
| 55 | + |
| 56 | + all functional tests go into this directory |
| 57 | + |
| 58 | + * test/java |
| 59 | + |
| 60 | + all unit tests go into this directory |
| 61 | + |
| 62 | + * test/resources |
| 63 | + |
| 64 | + 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. |
| 65 | + |
| 66 | + To make _idea plugin_ (and _eclipse plugin_) works, we also have this defined in hibernate-core.gradle: |
| 67 | + |
| 68 | + sourceSets { |
| 69 | + matrix { |
| 70 | + java { |
| 71 | + srcDir 'src/matrix/java' |
| 72 | + } |
| 73 | + resources { |
| 74 | + srcDir 'src/matrix/resources' |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + ideaModule { |
| 80 | + sourceDirs += file( '$buildDir/generated-src/antlr/main' ) |
| 81 | + testSourceDirs += file( 'src/matrix/java') |
| 82 | + testSourceDirs += file( 'src/matrix/resources') |
| 83 | + } |
| 84 | + |
| 85 | + |
| 86 | +3. DB profile |
| 87 | + |
| 88 | + 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. |
| 89 | + |
| 90 | + A DB profile looks like this: |
| 91 | + |
| 92 | + ├── mysql50 |
| 93 | + │ ├── jdbc |
| 94 | + │ │ └── mysql-connector-java-5.1.9.jar |
| 95 | + │ ├── matrix.gradle |
| 96 | + │ └── resources |
| 97 | + │ └── hibernate.properties |
| 98 | + |
| 99 | + 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: |
| 100 | + |
| 101 | + jdbcDependency "mysql:mysql-connector-java:5.1.17" |
| 102 | + |
| 103 | + 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. |
| 104 | + |
| 105 | + 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). |
| 106 | + |
| 107 | + NOTE: this `hibernate.properties` will overrides the one in `hibernate-core/test/resources/hibernate.properties` if same property name defined in both place. |
| 108 | + |
| 109 | + And, the DB profile name is the directory name, in this example, it is _mysql50_. |
| 110 | + |
| 111 | + 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. |
| 112 | + |
| 113 | +#### Matrix Tasks |
| 114 | + |
| 115 | +Once you have DB profiles defined, you could run `gradle tasks --all`, this will list all tasks you can use. |
| 116 | + |
| 117 | +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`. |
| 118 | + |
| 119 | +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`). |
| 120 | + |
| 121 | +Matrix test results are in `target/matrix/${profile_name}/test-results`. |
| 122 | + |
| 123 | +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_). |
| 124 | + |
| 125 | +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. |
| 126 | + |
| 127 | +#### Configuration Properties |
| 128 | + |
| 129 | +There are two way to pass a system property to gradle build: |
| 130 | + |
| 131 | + 1. The original java way, -Dxxx=yyy |
| 132 | + 2. Add it to ${user.home}/.gradle/gradle.properties with a 'systemProp' prefix, like 'systemProp.xxx=yyy' |
| 133 | + |
| 134 | +* hibernate-matrix-databases |
| 135 | + |
| 136 | + This property is used to define the location of DB profile container. |
| 137 | + Accept value: absolute path of DB profile container directory. |
| 138 | + |
| 139 | +* hibernate-matrix-ignore |
| 140 | + |
| 141 | + 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. |
| 142 | + |
| 143 | + Accept value : _all_ or _${profile name1},${profile name2},${profile name3}_ |
| 144 | + |
| 145 | + |
| 146 | +#### DB Allocator (JBoss internally, VPN required) |
| 147 | + |
| 148 | +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. |
| 149 | + |
| 150 | +And the connection info can be queried from DB Allocator automatically. |
| 151 | + |
| 152 | +This feature is disabled by default, to enable it, you need this system property |
| 153 | + |
| 154 | +* hibernate-matrix-dballcoation |
| 155 | + |
| 156 | + Accept value: _all_ or _${profile name1},${profile name2},${profile name3}_ |
| 157 | + |
| 158 | +For example, if you want to run matrix test on postgresql84, you can use this command |
| 159 | + |
| 160 | + ./gradlew clean test matrix_postgresql84 -Dhibernate-matrix-dballocation=postgresql84 |
| 161 | + |
| 162 | + what does this command do actually? |
| 163 | + 1. test |
| 164 | + run 'src/test/java' on default H2, test results in 'target/test-results' |
| 165 | + run 'src/matrix/java' on default H2, test results in 'target/test-results' |
| 166 | + |
| 167 | + 2. query postgresql 84 db instance connection info |
| 168 | + 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' |
| 169 | + |
| 170 | +Some DBs need we tweak url with some configurations after get it from DB allocator, so, we can use this system property: |
| 171 | + |
| 172 | +* hibernate-matrix-dballocation-url-postfix-${dbname} |
| 173 | + |
| 174 | + for example: |
| 175 | + |
| 176 | + `-Dhibernate-matrix-dballocation-url-postfix-sybase155="?SQLINITSTRING=set quoted_identifier on&DYNAMIC_PREPARE=true"` |
| 177 | + |
| 178 | +* hibernate-matrix-dballocation-requestee |
| 179 | + This property is used to define the DB Allocator requester name, default is _hibernate_ |
0 commit comments