Part 4: Testing¶
Plugins are standalone software that pipeline developers need to trust. Testing each feature independently, outside of a pipeline, ensures the plugin works correctly before anyone integrates it into a workflow. In this section, you'll write and run tests using the Spock testing framework.
Starting from here?
If you're joining at this part, copy the solution from Part 3 to use as your starting point:
Then change into the plugin directory:
Make sure you're in the plugin directory:
1. Why test?¶
A successful build means the code compiles, but doesn't check that it works as expected.
Unit tests are small pieces of code that automatically check if your functions produce the right output for a given input.
For example, a test might check that reverseGreeting("Hello") returns "olleH".
Tests are valuable because:
- They catch bugs before users do
- They give you confidence to make changes without breaking things
- They serve as documentation showing how functions should be used
2. Understanding Spock tests¶
The plugin template uses Spock, a testing framework for Groovy.
Spock is already configured in the project (via build.gradle), so you don't need to add anything.
If you've used testing tools before (like pytest in Python or testthat in R), Spock fills the same role: you write small functions that call your code with known inputs and check the outputs.
The difference is that Spock uses labelled blocks (given:, expect:, when:, then:) that are similar to a Nextflow process or workflow.
Here's the basic structure:
def 'should reverse a greeting'() { // (1)!
given: // (2)!
def ext = new GreetingExtension()
expect: // (3)!
ext.reverseGreeting('Hello') == 'olleH'
}
- Test name in quotes: Describes what the test checks. Use plain English.
given:block: Set up what you need for the test (create objects, prepare data)expect:block: The actual checks. Each line should betruefor the test to pass
This structure makes tests readable: "Given an extension object, expect that reverseGreeting('Hello') equals 'olleH'."
3. Write the tests¶
Write tests for the two functions you created in Part 3: reverseGreeting and decorateGreeting.
3.1. Create the test class¶
Open it in your editor and add the empty test class skeleton:
| src/test/groovy/training/plugin/GreetingExtensionTest.groovy | |
|---|---|
- All Spock test classes extend
Specification. This is the starting point for any Spock test file.
3.2. Test reverseGreeting¶
Add a test method inside the class body.
The given: block creates a GreetingExtension instance, and the expect: block checks that reverseGreeting correctly reverses two different inputs.
This tests the function directly, without running a pipeline.
- Create an instance of your extension to test directly, without running a pipeline
- Each line in
expect:is an assertion; the test passes only if all aretrue
3.3. Test decorateGreeting¶
Add a second test method after the first one.
This one verifies that decorateGreeting wraps the input string with *** on each side.
4. Run the tests¶
Test output
Where are the test results? Gradle hides detailed output when all tests pass. "BUILD SUCCESSFUL" means everything worked. If any test fails, you'll see detailed error messages.
Add an edge case test
Add a test that checks reverseGreeting handles an empty string.
What should reverseGreeting('') return?
Add the test, run make test, and verify it passes.
5. View the test report¶
Gradle generates an HTML test report with detailed results for each test. Start a web server in the report directory:
VS Code will prompt you to open the application in your browser. Click through to your test class to see individual test results:

The report shows each test method and whether it passed or failed.
Press Ctrl+C to stop the server, then return to the previous directory:
Go back to the main project directory:
Takeaway¶
You learned that:
- Spock tests use a readable
given:/expect:structure - Use
make testto run tests andbuild/reports/tests/test/for the HTML report - Tests verify behavior and serve as documentation for how functions should be used
What's next?¶
So far, your plugin adds custom functions that pipelines can call. Plugins can also react to workflow events (a task completing, a file being published, the pipeline finishing) using trace observers. In the next section, you'll build an observer that counts completed tasks and prints a summary when the pipeline finishes.