The idea of this plugin is to extend Gradle with the idea of named database profiles that a user could choose between when running tests. This allows the project to define a single set of tests but have them run them against a dynamic set of databases.
plugins { ... id 'org.hibernate.testing.database-profile' version 'X.Y.Z' }
This applies a DSL extension of type DslExtension
to the project under the name databases
. The following
sections will break down the available options…
A profile defines the information needed to run the tests against a particular database instance. This includes information like any dependencies needed, the connection URL, etc.
Profile
is the class describing a profile. A profile can be defined in a few different ways,
but all will ultimately create a Profile
which all get added to the DslExtension#profiles
attribute which is a Gradle NamedDomainObjectContainer
.
A profile can be defined directly in the databases
DSL extension. E.g.:
build.gradle
databases { ... profiles { // creates a Profile named "h2" h2 { hibernateProperty 'hibernate.connection.url', 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000' hibernateProperty 'hibernate.connection.username', 'sa' hibernateProperty 'hibernate.connection.password', 'sa' dependency 'com.h2database:h2:1.4.196' } } }
Profiles can also be defined using a Gradle fragment file. The fragment is applied relative to the
profiles
NamedDomainObjectContainer
The selection of a profile might imply a number of
actions that must occur based on the profile selected - we might need to add extra jars to the
testRuntime
configuration, adjust properties files, etc.
When applied, the plugin will:
-
Create a task named
applyDatabaseProfile
of typeProfileTask
-
For every available profile (see below), create a task named
processTestResources_<profile_name>
and a task namedtest_<profile_name>
which do what they sound like they’d do :)
applyDatabaseProfile
is added to processTestResources
as a finalizer.
Each test_<profile_name>
task is finalized by test
.
Each processTestResources_<profile_name>
task depends on processTestResources
and is finalized by applyDatabaseProfile
Only one such `processTestResources_<profile_name>` or `test_<profile_name>` can be done at once
Available profiles are resolved using a directory search, as follows:
-
If the project is non-root, see if it defines a
${projectDir}/databases/
directory. If it does then search here first -
If any custom search directories have been added, check these next. Custom directories can be added by specifying a
custom-profiles-dir
build property -
Finally look at the project’s parent project for a
${projectDir}/databases/
directory, up to the root project
These directories are searched recursively. We leverage this in Hibernate to allow the standard databases directory to hold local profiles too. That is achieved by a .gitignore which says to ignore any directory named local under the directory databases. So one option to provide custom profiles is to drop them in there. That has the benefit of not having to specify custom_profiles_dir build property.
Within these directories, the plugin looks for sub-directories which either:
-
contain a file named matrix.gradle. matrix.gradle is a limited DSL Gradle file which currently understands just a specialized org.gradle.api.artifacts.Configuration reference named jdbcDependency. All that is a fancy way to say that matrix.gradle allows you to specify some dependencies this database profile needs (JDBC drivers, etc). Any dependency artifacts named here get resolved using whatever resolvers (Maven, etc) are associated with the build. For example
jdbcDependency { "mysql:mysql-connector-java:5.1.17" } * contain a directory named _jdbc_ which is assumed to hold jar file(s) needed for the profile.
Such directories become the basis of a database profile made available to the build. The name of the profile is taken from the directory name. Database profiles can also contain a resources directory.
An example layout using jdbc directory approach:
├── mysql50 │ ├── jdbc │ │ └── mysql-connector-java-5.1.9.jar │ └── resources │ └── hibernate.properties
versus using matrix.gradle:
├── mysql50 │ ├── matrix.gradle │ └── resources │ └── hibernate.properties
Either would result in a database profile named mysql50
==Specifying the profile to use
The profile to use can be specified in 2 ways:
-
Set
database_profile_name
build property to the name of the profile to use -
Calling one of the
processTestResources_<profile_name>
ortest_<profile_name>
tasks
During evaluation phase, the plugin locates all available profiles (see ProfileLoader
) and
creates the applyDatabaseProfile
task (see ProfileTask
).
It delays as long as possible the determination of the specific profile to use. This is needed
to make processTestResources_<profile_name>
and test_<profile_name>
tasks work.
If no profile name is specified the behavior is to not perform any of the task actions.
The task can contain 3 types of actions:
-
ProfileTask#augment
-
ProfileTask#filterCopy
-
ProfileTask#extend
By default the plugin will overlay the profile’s properties over
top of the ${buildDir}/resources/test/hibernate.properties
file. This is a
process called augmentation - a properties file is loaded into a Properties
object and then the profile’s properties are added over top of them and then
written back out.
A custom properties file augmentation can be requested using ProfileTask#augment
.
Builds can also request a filtered-copy using ProfileTask#filterCopy
. A filtered-copy
is basically a copy based on the provided CopySpec config closure extended profile properties
replacements. E.g., Hibernate ORM uses this to process the bundles
directory used
to test JPA deployments) - used to replace info in XML, properties, etc
Lastly, pretty generic actions can be requested using ProfileTask#extend
which
accepts an Action<Profile>
- during doLast
, the task will call this action with
the resolved profile.
Personally I plan to look again into having IntelliJ delegate to Gradle. That used to be dog slow, but maybe better today….
Anyway, you can also continue using the old .. processTestResources -Pdb=derby copyResourcesToIntelliJOutFolder
approach. You can even do … processTestResources_h2 copyResourcesToIntelliJOutFolder
. Once applyDatabaseProfile
has applied the profile it will not re-apply the profile next time if either (a) profile name is null or (b) profile name is the same