Automation Testing Framework
With the Automation Testing Framework, you can test the logic of scripts that use the published APIs using common testing frameworks like JUnit or Spock, while running them with the functionality of DaVinci Configurator 6.
How to enable the testing framework
scriptProject {
cfgPath = file("${cfgPath}")
classes = ["MyScript"]
testing {
bswPath = file("${bswPath}")
}
}
Additionally the testing dependencies have to be added.
Managing Dependencies
For adding test dependencies to your project, you should use the testCfg configuration.
testCfgImplementation libs.spock
testCfgImplementation platform(libs.junitBom)
testCfgImplementation libs.junitApi
testCfgImplementation libs.junitEngine
The template contain some of them with a compatible version in the libs.version.toml file, so you can use them directly in your build.gradle file.
Writing basic tests
You can write tests using a common testing framework of your choice. While Spock is the preferred method, JUnit works as well.
These tests must be located in the src/testCfg directory of your script project.
The example GetDpaProjectNameTest shows a Spock test that loads a DaVinci Configurator 6 project using the @TestProjectLoad annotation.
All available loading scenarios are described in Loading CFG6 Projects in Tests below. For more control over the test task itself, see Custom Automation Script Tests.
class GetDpaProjectNameTest extends Specification {
@Shared
IProjectRef projectRef = ScriptApi.scriptCode.projects.parameterizeProjectLoad {
it.projectFile = Paths.get("%CFG_PROJECT_PATH%")
}
@Shared
@TestProjectLoad
ITestProjectRef projectRefExtShared = ITestProjectRef.forProject(projectRef)
def "Test read project name"() {
when:
String origin = GetProjectName.getDpaProjectName()
then:
origin == "WorkshopProject"
}
static String getDpaProjectName() {
def name = ""
ScriptApi.scriptCode {
projects.activeProject {
name = project.getProjectName()
scriptLogger.info("Work with the project: " + name + ".dvjson")
}
}
return name
}
}
The %CFG_PROJECT_PATH% placeholder must be replaced with the path of the DaVinci Configurator 6 project you want to test.
Model TestInfrastructure
The ITransactionUndoAllExtension class provides a JUnit Extension, which will undo all transaction after the
JUnit test was executed. This can be used as static or instance field to undo for the whole test class or for each test case.
Note: This extension does not work with Spock.
public class ProjectLoadTest {
@RegisterExtension
static ITransactionUndoAllExtension extension = ITransactionUndoAllExtension.forActiveProject(); // Undo changes after ALL test cases.
public class ProjectLoadTest {
@RegisterExtension
ITransactionUndoAllExtension extension = ITransactionUndoAllExtension.forActiveProject(); // Undo changes after EACH test case.
class TransactionUndoAllExtensionTest {
@RegisterExtension
ITransactionUndoAllExtension rule = ITransactionUndoAllExtension.forActiveProject();
@Test
void testcase() {
//After this test case all transactions are reverted by the TransactionUndoAllExtension
}
class TransactionUndoAllExtensionWithLimitTest {
@RegisterExtension
ITransactionUndoAllExtension rule = ITransactionUndoAllExtension.forActiveProject(50);
@Test
void testcase() {
//After this test case all transactions are reverted by the TransactionUndoAllExtension
}
Loading CFG6 Projects in Tests
Spock
- Simple — once per test class — Show example
- Simple — once per test method — Show example
- Standard — once per test class — Show example
- Standard — once per test method — Show example
- Advanced — once per class + once per method — Show example
- Suite — once per test task — Show example
class LoadProjectTest extends Specification {
@Shared
@TestProjectLoad
ITestProjectRef testProjectRef = ITestProjectRef.loadProject("%CFG_PROJECT_PATH%")
def "Load project via loadProject shorthand"() {
when:
def projectName = testProjectRef.project.getProjectName()
then:
projectName == "WorkshopProject"
}
}
class LoadProjectPerTestExampleTest extends Specification {
@TestProjectLoad
ITestProjectRef testProjectRef = ITestProjectRef.loadProject("%CFG_PROJECT_PATH%")
def "Load project per test method via loadProject shorthand"() {
when:
def projectName = testProjectRef.project.getProjectName()
then:
projectName == "WorkshopProject"
}
}
class ForProjectSharedExampleTest extends Specification {
@Shared
IProjectRef projectRef = ScriptApi.scriptCode.projects.parameterizeProjectLoad {
it.projectFile = Paths.get("%CFG_PROJECT_PATH%")
}
@Shared
@TestProjectLoad
ITestProjectRef testProjectRef = ITestProjectRef.forProject(projectRef)
def "Load project per test class via forProject"() {
when:
def projectName = testProjectRef.project.getProjectName()
then:
projectName == "WorkshopProject"
}
}
class ForProjectPerTestExampleTest extends Specification {
@Shared
IProjectRef projectRef = ScriptApi.scriptCode.projects.parameterizeProjectLoad {
it.projectFile = Paths.get("%CFG_PROJECT_PATH%")
}
@TestProjectLoad
ITestProjectRef testProjectRef = ITestProjectRef.forProject(projectRef)
def "Load project per test method via forProject"() {
when:
def projectName = testProjectRef.project.getProjectName()
then:
projectName == "WorkshopProject"
}
}
class ForProjectAdvancedExampleTest extends Specification {
@Shared
IProjectRef projectRef = ScriptApi.scriptCode.projects.parameterizeProjectLoad {
it.projectFile = Paths.get("%CFG_PROJECT_PATH%")
}
@Shared
@TestProjectLoad
ITestProjectRef testProjectRefShared = ITestProjectRef.forProject(projectRef)
@TestProjectLoad
ITestProjectRef testProjectRefPerTest = ITestProjectRef.forProject(projectRef)
def "Access project from both class-scoped and method-scoped ref"() {
expect:
testProjectRefShared.project.getProjectName() == "WorkshopProject"
testProjectRefPerTest.project.getProjectName() == "WorkshopProject"
}
}
Load a Project Once for multiple Tests in a Test Run
When your tests need a DaVinci Configurator 6 project open throughout the entire test run — without reloading it for every test method — you can create a SharedCfgProject class.
The class implements the JUnit Platform LauncherSessionListener and TestExecutionListener interfaces.
It loads the project once before all tests start and closes it after all tests finish.
This works with both Spock and JUnit Jupiter tests in the same test run.
Step 1: Create SharedCfgProject.groovy
Place the following class in src/testCfg/groovy/SharedCfgProject.groovy:
import com.vector.cfg.automation.api.ScriptApi
import com.vector.cfg.automation.scripting.api.project.IProject
import org.junit.platform.launcher.LauncherSession
import org.junit.platform.launcher.LauncherSessionListener
import org.junit.platform.launcher.TestExecutionListener
import org.junit.platform.launcher.TestPlan
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicReference
class SharedCfgProject implements LauncherSessionListener, TestExecutionListener {
private static final AtomicReference<IProject> PROJECT = new AtomicReference<>()
// Returns false when %CFG_PROJECT_PATH% has not been replaced with an actual path,
// keeping this class a no-op in projects that do not use a shared CFG6 project.
private static boolean isConfigured() {
return !"%CFG_PROJECT_PATH%".contains("%")
}
private static IProject initProject() {
def projectRef = ScriptApi.scriptCode().projects.parameterizeProjectLoad {
it.projectFile = Paths.get("%CFG_PROJECT_PATH%")
}
def project = projectRef.advanced().openProject()
println "Loading shared CFG6 project used for tests."
return project
}
static synchronized IProject getProject() {
if (!isConfigured()) {
return null
}
def project = PROJECT.get()
if (project == null) {
project = initProject()
PROJECT.set(project)
}
return project
}
@Override
void launcherSessionOpened(LauncherSession session) {
session.launcher.registerTestExecutionListeners(this)
}
@Override
void testPlanExecutionStarted(TestPlan testPlan) {
getProject()
}
@Override
void testPlanExecutionFinished(TestPlan testPlan) {
def project = PROJECT.getAndSet(null)
if (project == null) {
return
}
try {
project.close()
} catch (Exception ignored) {
}
println "Shared CFG6 project unloaded after tests."
}
}
Replace %CFG_PROJECT_PATH% with the path to your DaVinci Configurator 6 project file.
Step 2: Register the service
Create the file src/testCfg/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener with the following content:
SharedCfgProject
Using a Shared CFG6 Project in Tests
The shared CFG6 project is set as the active project, so your tests can access it using the standard
ScriptApi.scriptCode.projects.activeProject.project API — no additional setup is required in individual test classes.
JUnit
Load a CFG6 Project
The IProjectLoadExtension class provides a JUnit 5 Extension, which will load the passed IProjectRef
before test execution and close it afterward. This can be used as an JUnit 5 Extension.
public class ProjectLoadExtensionTest {
@RegisterExtension
static IProjectLoadExtension extension = IProjectLoadExtension.forProject(ScriptApi.scriptCode.projects.parameterizeProjectLoad{
projectFile "YourProject.dvjson"
})
@Test
public void testcase() {
//In this test the project was loaded and will be closed after all tests.
}
For details about the how to load a CFG6 project, or if you want to load only
an .arxml file, please refer to the Automation Interface User Documentation.
Custom Automation Script Tests
If you want to have more flexibility in your tests, you can write custom tests that use the AutomationScriptTest task directly. This allows you to specify multiple test classes in different source sets and have more control over the test execution.
import com.vector.cfg.pai.testexecution.AutomationScriptTest
import org.gradle.api.plugins.JavaPlugin
def customSourceSetName = "myTest"
sourceSets {
create(customSourceSetName){
compileClasspath += sourceSets.main.output
}
}
configurations {
myTestImplementation.extendsFrom(implementation)
myTestCompileOnly.extendsFrom(compileOnly)
}
dependencies {
myTestImplementation libs.spock
myTestImplementation libs.junitApi
myTestImplementation libs.junitEngine
myTestImplementation platform(libs.junitBom)
myTestRuntimeOnly libs.junitPlatformLauncher
}
tasks.register("myTestTaskStandardBsw", AutomationScriptTest){
it.sourceSetName = customSourceSetName
it.bswPath.set(myBswPath)
it.configureCfgPath(myCfgPath)
it.dependsOn(tasks.named("jar"))
}
tasks.register("myTestSecondBsw", AutomationScriptTest){
it.sourceSetName = customSourceSetName
it.bswPath.set(mySecondBswPath)
it.configureCfgPath(myCfgPath)
it.dependsOn(tasks.named("jar"))
}
Plugin configuration
Following properties can be set in the testCfg configuration:
cfgPath property specifies the path to the DaVinci Configurator 6
which should be used to run the tests.
The bswPath property specifies the path to the BSW package
which is used to run the tests.
The additionalVmArgs property allows you to specify
additional JVM arguments for the test execution.
Source Set 'testCfg'
The testing framework is based on the
testCfg source set[1], which is automatically enabled when you activate the testing framework in your project.
See How to enable the testing framework for more information.
In addition to the testCfg source set, the standard test source set can be used without script support.
Gradle Task 'testCfg'
To run the tests, you can use the gradlew testCfg command.
This gradle task will execute all tests in the testCfg source set.
The testCfg task will automatically get executed when you run the build task.
Test and Debug productive Tasks with the Testing Framework
To test and debug a script task in a fast and convenient way, you can use the testing framework.
For this, a test will be set up that executes the task you want to test or debug. You can set breakpoints in your script and debug the test.
With the testing framework, you have control over the task execution environment. This means you can provide a bsw package, a DaVinci Configurator 6 project and user defined arguments.
Attention: Tests which executes a whole script task can take some time, especially if a project will be loaded. You can classify or disable these test with the common Spock/Junit mechanisms.
Test a DV_APPLICATION task
To call a DV_APPLICATION task:
scriptTask("SimpleTask", DV_APPLICATION) {
taskDescription 'Task description - Prints "HelloWorld" to console.'
code {
scriptLogger.info "!!!Hello World!!!"
return "SimpleTask executed"
}
}
You can write a spock test and test/debug the task:
def "Debug simple task"() {
when:
def result = ScriptApi.scriptCode().scripts.callScriptTask("SimpleTask")
then:
result == "SimpleTask executed"
}
Test a task with userdefined arguments
To call a task with userdefined arguments:
scriptTask("SimpleTaskWithParams", DV_APPLICATION) {
def nameArg = newUserDefinedArgument("name", String, "My String Param")
def value = newUserDefinedArgument("value", Integer, "My Integer Param")
code {
return "name: $nameArg.value, value: $value.value"
}
}
You can write a spock test and test/debug the task:
def "Debug task with params"() {
when:
def result = ScriptApi.scriptCode().scripts.callScriptTaskWithUserArgs("SimpleTaskWithParams", "--name=Hello --value=42")
then:
result == "name: Hello, value: 42"
}
Test a DV_PROJECT task
To call a DV_PROJECT task:
scriptTask("ProjectTask", DV_PROJECT) {
code {
activeProject().getProjectName()
}
}
You can write a spock test and test/debug the task:
def "Debug task with project"() {
when:
def result = ScriptApi.scriptCode().projects.openProject("%CFG_PROJECT_PATH%") { scripts.callScriptTask("ProjectTask") }
then:
result == "WorkshopProject"
}
Attention:
The project will not be saved by default. But if the script contains asaveProjectcall, the project will be saved and persisted. See