JUnit5 plugin to run pytest via Gradle
Allows running pytest from Gradle, either locally or via Gradle Enterprise's Distributed Testing.
How to use it
Example
import com.datastax.junitpytest.gradleplugin.Pytest
plugins {
id("com.datastax.junitpytest") version "0.1-SNAPSHOT"
id("com.gradle.enterprise.test-distribution")
}
repositories {
mavenCentral()
mavenLocal()
}
pytest { // Extension type: com.datastax.junitpytest.gradleplugin.PytestExtension
pytestDirectorySet.srcDir("src/test/python")
}
dependencies {
testImplementation("org.junit.platform:junit-platform-launcher:${pytest.junitPlatformVersion.get()}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${pytest.junitJupiterVersion.get()}")
testImplementation("org.assertj:assertj-core:3.15.0")
}
tasks.named<Pytest>("pytest") { // Note: Pytest extends Test
pytestOptions = listOf("--some-option-passed-to-pytest",
"--another-pytest-option")
// Gradle Enterprise distributed testing
distribution {
enabled.set(false)
maxLocalExecutors.set(0)
maxRemoteExecutors.set(20)
}
}
PytestExtension
registered as pytest
Common properties of the PytestExtension
extension:
junitPlatformVersion
(Property<String>
): the JUnit platform version, defaults to1.6.2
, for running pytestsjunitJupiterVersion
(Property<String>
): the JUnit Jupiter version, defaults to5.6.2
, for running pytestspytestDirectorySet
(SourceDirectorySet
): users must specify exactly onesrcDir
, default excludes:logs
,venv
,.git
,.idea
,**/__pycache__
,**/.*.swp
,.pytest_cache
requirementsSource
(RegularFileProperty
): location of therequirements.txt
file to use, defaults torequirements.txt
inpytestDirectorySet
The plugin registers a pytest
(type: com.datastax.junitpytest.gradleplugin.Pytest
) task. Options for pytest can be specified using Pytest.pytestOptions
(ListProperty<String>
).
If you want to have an additional pytest-task using different pytest-options, use com.datastax.junitpytest.gradleplugin.PytestExtension.createTasks(baseName: String)
to register another Pytest
task. Tasks will be named using camel case with the baseName
appended to pytest
. For example, createTasks("other")
will create the pytestOther
task.
Using editable requirements
So called "editable requirements" (i.e. those installed via pip install --editable
) can be handled specially using GitSource
and LocalSource
tasks, which can be registered in the pytest
Gradle extension using the sourceRequirementsTasks
property (type: ListProperty<TaskProvider<out RepoSource>>
). Note that the LocalSource
task extends Gradle's built-in Sync
task.
The local Gradle daemon pulls the latest HEAD from git (if using GitSource
) and syncs the locally available source (if using a LocalSource
).
It's recommended to have a requirements-file that only contains the "released" requirements (non-source requirements) and rebgister this in the pytest
Gradle extension using the requirementsSource
property (type: RegularFileProperty
) and use GitSource
/LocalSource
for source-requirements. Otherwise, each Gradle distributed test agent will pull the requirements themselves. In other words: GitSource
/LocalSource
make the source requirements available to all Gradle distributed test-agents.
GitSource
task
Necessary properties:
repository: Property<String>
Specifiy the git repository URL (as it appears in arequirements.txt
file)eggName: Property<String>
Specify the name of the pip "egg" as it appears in arequirements.txt
filebranch: Property<String>
Specify the git branch, tag or git SHA
LocalSource
task
Necessary properties:
repository: Property<String>
The source as it appears in arequirements.txt
fileeggName: Property<String>
Specify the name of the pip "egg" as it appears in arequirements.txt
filesourceDirectory: DirectoryProperty
Specify the directory containing the source of the requirement
How it works
- Create a Python virtual environment (Gradle task
com.datastax.junitpytest.gradleplugin.PytestCreateVirtualenv
) - Test discovery (Gradle task
com.datastax.junitpytest.gradleplugin.PytestDiscovery
)- Use
pytest
+junit-pytest-plugin
to collect all test classes and test cases - Map the Python module + class + test-method names into Java namespace
- Generate Java class files (methods are empty)
- Use
- Test execution (Gradle task
com.datastax.junitpytest.gradleplugin.Pytest
)- Use a custom
org.gradle.api.tasks.testing.Test
based task to run the tests using thepytest-junit-engine
- Use a custom
Internal tasks
The plugin creates two more types of internal tasks, which should not be referenced/used/configured by a user:
- A
PytestDiscovery
task for eachPytest
task to run test discovery via pytest only when the Python sources changed. - A
PytestCreateVirtualenv
task for the project to setup the virtual environment (if necessary), install the junit-pytest-plugin + dependencies fromrequirements.txt
and to create the "frozen" list of dependencies usingpip freeze
.
Gradle Enterprise Distributed Testing
Gradle Enterprise (as of version 2020.2) Distributed Testing requires JUnit 5 to run tests and tests must be self-contained.
Since Python setups (think: virtual environments) are machine dependent, no actual Python installation is passed around. Instead, the Gradle tasks create a virtual environment on the machine running the Gradle daemon, install the necessary requirements (pip install -r requirements.txt
), collect the installed dependencies (pip freeze
) and install the "frozen requirements" on the test agents.
This approach ensures that the test agents use the same dependencies as the machine that started the build. However, it may not ensure that no other dependencies, that are not mentioned via requirements.txt
, are installed on the test agents.
Modules (sub-projects)
:pytest-gradle-plugin
the Gradle plugin to wire an extension and some tasks to run Python test via Gradle (published as a maven-publication):pytest-junit-engine
the JUnit engine that can run Python tests (published as a maven-publication):junit-pytest-plugin
thepytest
plugin to emit test events to the:pytest-junit-engine
(not published, included in the Gradle plugin and junit-engine):common
some Java classes used by:pytest-gradle-plugin
and:pytest-junit-engine
(not published, included in the Gradle plugin and junit-engine)
Build requirements
- Recent Linux or OSX
- Java 8 or newer (Java 11 is fine)
- Python >= 3.6 (with python3 and pip3 and virtualenv in
PATH
environment)
Background information
pytest-junit-engine
The pytest-junit-engine
requires some configuration options. From Gradle, those are passed using system properties.
pytest.venv
The directory for the Python virtual environmentpytest.frozenRequirements
File containing the output ofpip freeze
pytest.pytestOutputs
Directory receiving additional output files from Python tests added viajunitpytest.gradle.register_outputs(files_or_dirs)
pytest.cwd
Current working directory when executingpytest
pytest.exec.virtualenv
Comma separated list of executable file names (looked up viaPATH
)pytest.exec.python
Comma separated list of executable file names (looked up viaPATH
)pytest.keepOutputForPassed
When set totrue
, keep the output even for tests that passed.pytest.debug
When set totrue
, enables additional debug outputpytest.option.<N>
Additional command line options forpytest
.<N>
starts with0
, so the first argument ispytest.option.0
, the secondpytest.option.1
, etc.pytest.env.<N>
Additional environment variables forpytest
. Values are in the formVAR=VALUE
<N>
starts with0
, so the first argument ispytest.env.0
, the secondpytest.env.1
, etc.pytest.pip.option.<N>
Additional command line options forpip
.<N>
starts with0
, so the first argument ispytest.pip.option.0
, the secondpytest.pip.option.1
, etc.pytest.pip.env.<N>
Additional environment variables forpip
. Values are in the formVAR=VALUE
<N>
starts with0
, so the first argument ispytest.pip.env.0
, the secondpytest.pip.env.1
, etc.pytest.source.<N>
Additional requirements to be installed usingpip install --source
. Values are in the formrepository=relative-path
.repository
is the value of the property of theGitSource
/LocalSource
tasks.<N>
starts with0
, so the first argument ispytest.source.0
, the secondpytest.source.1
, etc.
License and Copyright
Copyright DataStax, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.