Apache Groovy is a powerful multi-faceted programming language for the JVM platform
—
Comprehensive testing framework with enhanced JUnit integration, mocking capabilities, and Groovy-specific assertion utilities. Provides GroovyTestCase for improved test organization and mock objects for isolation testing.
Enhanced test case base class that extends JUnit with Groovy-specific testing features and improved assertion methods.
/**
* Enhanced test case class with Groovy-specific testing utilities
*/
abstract class GroovyTestCase extends TestCase {
/**
* Create test case with name
*/
GroovyTestCase();
GroovyTestCase(String name);
/**
* Assert that code execution throws expected exception
*/
void shouldFail(Closure code);
void shouldFail(Class<? extends Throwable> expectedType, Closure code);
void shouldFail(String expectedMessage, Closure code);
/**
* Assert that code executes without throwing exception
*/
void shouldNotFail(Closure code);
/**
* Assert equality with Groovy-aware comparison
*/
void assertEquals(Object expected, Object actual);
void assertEquals(String message, Object expected, Object actual);
/**
* Assert collection contents regardless of order
*/
void assertArrayEquals(Object[] expected, Object[] actual);
void assertCollectionEquals(Collection expected, Collection actual);
/**
* Assert script compilation and execution
*/
void assertScript(String script);
void assertScript(String script, Object expectedResult);
/**
* Get GroovyShell for script testing
*/
GroovyShell getShell();
/**
* Execute Groovy script in test context
*/
Object evaluate(String script);
}Usage Examples:
import groovy.test.GroovyTestCase
class MathUtilsTest extends GroovyTestCase {
void testBasicArithmetic() {
def calculator = new Calculator()
assertEquals(5, calculator.add(2, 3))
assertEquals(6, calculator.multiply(2, 3))
assertEquals(2.5, calculator.divide(5, 2))
}
void testDivisionByZero() {
def calculator = new Calculator()
shouldFail(ArithmeticException) {
calculator.divide(10, 0)
}
shouldFail("Division by zero") {
calculator.safeDivide(10, 0)
}
}
void testCollectionOperations() {
def list1 = [1, 2, 3]
def list2 = [3, 1, 2] // Different order
assertCollectionEquals(list1, list2) // Order doesn't matter
def array1 = [1, 2, 3] as int[]
def array2 = [1, 2, 3] as int[]
assertArrayEquals(array1, array2)
}
void testScriptExecution() {
// Test script compilation
assertScript '''
def x = 5
def y = 10
assert x + y == 15
'''
// Test script with expected result
assertScript 'return 2 + 3', 5
// Use shell for complex testing
shell.setVariable('testData', [a: 1, b: 2])
def result = evaluate('testData.a + testData.b')
assertEquals(3, result)
}
void testStringOperations() {
def text = "Hello World"
assertTrue(text.contains("World"))
assertFalse(text.startsWith("world")) // Case sensitive
assertEquals(11, text.length())
}
}Create mock objects and stubs for isolation testing with behavior verification.
/**
* Creates mock objects that verify method calls and return values
*/
class MockFor {
/**
* Create mock for specified class
*/
MockFor(Class clazz);
/**
* Define expected method call and return value
*/
void demand(String methodName);
void demand(String methodName, int callCount);
void demand(String methodName, int minCalls, int maxCalls);
/**
* Create proxy instance with mock behavior
*/
Object use(Closure closure);
/**
* Verify all expected calls were made
*/
void verify();
}
/**
* Creates stub objects that return predefined values without verification
*/
class StubFor {
/**
* Create stub for specified class
*/
StubFor(Class clazz);
/**
* Define method behavior without call verification
*/
void demand(String methodName);
void demand(String methodName, Closure returnValue);
/**
* Create proxy instance with stub behavior
*/
Object use(Closure closure);
}Usage Examples:
import groovy.mock.MockFor
import groovy.mock.StubFor
import groovy.test.GroovyTestCase
class EmailServiceTest extends GroovyTestCase {
void testEmailSending() {
// Mock the SMTP service
def mockSmtp = new MockFor(SmtpService)
// Define expected method calls
mockSmtp.demand.connect { host, port ->
assertEquals("smtp.example.com", host)
assertEquals(587, port)
return true
}
mockSmtp.demand.authenticate { username, password ->
assertEquals("user@example.com", username)
return true
}
mockSmtp.demand.sendMessage(1) { message ->
assertEquals("Test Subject", message.subject)
assertEquals("Test Body", message.body)
return "messageId123"
}
mockSmtp.demand.disconnect()
// Use mock in test
mockSmtp.use { smtp ->
def emailService = new EmailService(smtp)
def result = emailService.sendEmail(
to: "recipient@example.com",
subject: "Test Subject",
body: "Test Body"
)
assertEquals("messageId123", result)
}
// Verify all expected calls were made
mockSmtp.verify()
}
void testDatabaseConnectionStub() {
// Stub database connection for testing without real DB
def stubDb = new StubFor(DatabaseConnection)
stubDb.demand.query { sql ->
// Return fake data based on query
if (sql.contains("users")) {
return [[id: 1, name: "John"], [id: 2, name: "Jane"]]
}
return []
}
stubDb.demand.execute { sql ->
return 1 // Always return 1 row affected
}
stubDb.use { db ->
def userService = new UserService(db)
def users = userService.getAllUsers()
assertEquals(2, users.size())
assertEquals("John", users[0].name)
def result = userService.createUser("Bob", "bob@example.com")
assertEquals(1, result)
}
}
void testPartialMocking() {
// Mock only specific methods of a class
def mockFile = new MockFor(FileProcessor)
mockFile.demand.readFile { filename ->
if (filename == "config.properties") {
return "key1=value1\nkey2=value2"
}
throw new FileNotFoundException("File not found: $filename")
}
mockFile.demand.writeFile { filename, content ->
assertTrue(filename.endsWith(".backup"))
assertTrue(content.contains("value1"))
}
mockFile.use { fileProcessor ->
def configManager = new ConfigManager(fileProcessor)
configManager.backupConfig("config.properties")
}
}
}Organize and execute multiple test classes with comprehensive reporting.
/**
* Manages execution of multiple test classes
*/
class GroovyTestSuite extends TestSuite {
/**
* Create empty test suite
*/
GroovyTestSuite();
/**
* Create test suite with name
*/
GroovyTestSuite(String name);
/**
* Add test class to suite
*/
void addTestSuite(Class<? extends TestCase> testClass);
/**
* Add individual test method
*/
void addTest(Test test);
/**
* Load test suite from directory
*/
static Test suite();
static Test suite(String directory);
/**
* Run all tests in suite
*/
TestResult run();
TestResult run(TestResult result);
}
/**
* Utility for automatically discovering and running tests
*/
class AllTestSuite {
/**
* Create test suite from all test classes in package
*/
static Test suite();
/**
* Create test suite from specific directory
*/
static Test suite(String directory);
/**
* Create test suite with custom filter
*/
static Test suite(Closure filter);
}Usage Examples:
import groovy.test.GroovyTestSuite
import groovy.test.AllTestSuite
import junit.framework.TestResult
// Create custom test suite
class MyTestSuite extends GroovyTestSuite {
static Test suite() {
def suite = new GroovyTestSuite()
// Add individual test classes
suite.addTestSuite(MathUtilsTest)
suite.addTestSuite(StringUtilsTest)
suite.addTestSuite(DatabaseTest)
suite.addTestSuite(ApiTest)
return suite
}
}
// Run test suite programmatically
def suite = MyTestSuite.suite()
def result = suite.run()
println "Tests run: ${result.runCount()}"
println "Failures: ${result.failureCount()}"
println "Errors: ${result.errorCount()}"
// Auto-discover all tests in directory
class AutoTestSuite {
static Test suite() {
return AllTestSuite.suite("src/test/groovy")
}
}
// Create filtered test suite
class IntegrationTestSuite {
static Test suite() {
return AllTestSuite.suite { testClass ->
testClass.name.contains("Integration")
}
}
}
// Run with custom test runner
def runTestsWithReporting() {
def suite = AllTestSuite.suite()
def result = new TestResult()
// Add listeners for detailed reporting
result.addListener([
startTest: { test ->
println "Starting: ${test.name}"
},
endTest: { test ->
println "Completed: ${test.name}"
},
addError: { test, error ->
println "ERROR in ${test.name}: ${error.message}"
},
addFailure: { test, failure ->
println "FAILURE in ${test.name}: ${failure.message}"
}
])
suite.run(result)
return result
}Extended assertion methods for comprehensive test validation.
/**
* Enhanced assertion utilities for Groovy testing
*/
class GroovyAssert {
/**
* Assert that closure throws expected exception
*/
static void shouldFail(Closure code);
static void shouldFail(Class<? extends Throwable> expectedType, Closure code);
static void shouldFail(String expectedMessage, Closure code);
/**
* Assert collection equality ignoring order
*/
static void assertCollectionEquals(Collection expected, Collection actual);
static void assertCollectionEquals(String message, Collection expected, Collection actual);
/**
* Assert array equality
*/
static void assertArrayEquals(Object[] expected, Object[] actual);
static void assertArrayEquals(String message, Object[] expected, Object[] actual);
/**
* Assert script execution results
*/
static void assertScript(String script);
static void assertScript(String script, Object expectedResult);
/**
* Assert objects are approximately equal (for floating point)
*/
static void assertApproximatelyEqual(double expected, double actual, double tolerance);
/**
* Assert string matches pattern
*/
static void assertMatches(String pattern, String actual);
static void assertMatches(String message, String pattern, String actual);
}Usage Examples:
import static groovy.test.GroovyAssert.*
import groovy.test.GroovyTestCase
class AssertionExamplesTest extends GroovyTestCase {
void testExceptionHandling() {
// Test specific exception type
shouldFail(IllegalArgumentException) {
new BankAccount(-100) // Negative balance not allowed
}
// Test exception message
shouldFail("Insufficient funds") {
def account = new BankAccount(50)
account.withdraw(100)
}
// Test any exception
shouldFail {
def result = 10 / 0
}
}
void testCollectionAssertions() {
def list1 = [1, 2, 3, 4]
def list2 = [4, 3, 2, 1] // Different order
def list3 = [1, 2, 3] // Different size
// Collections equal regardless of order
assertCollectionEquals(list1, list2)
// Different sizes should fail
shouldFail {
assertCollectionEquals(list1, list3)
}
def array1 = ["a", "b", "c"] as String[]
def array2 = ["a", "b", "c"] as String[]
assertArrayEquals(array1, array2)
}
void testScriptAssertions() {
// Simple script validation
assertScript '''
def factorial = { n ->
n <= 1 ? 1 : n * factorial(n - 1)
}
assert factorial(5) == 120
'''
// Script with expected return value
assertScript 'return [1, 2, 3].sum()', 6
// Complex script testing
assertScript '''
class Person {
String name
int age
String toString() { "$name ($age)" }
}
def person = new Person(name: "John", age: 30)
return person.toString()
''', "John (30)"
}
void testNumericAssertions() {
def pi = 3.14159
def approximatePi = 22 / 7
// Floating point comparison with tolerance
assertApproximatelyEqual(pi, approximatePi, 0.01)
shouldFail {
assertApproximatelyEqual(pi, approximatePi, 0.001) // Too strict
}
}
void testPatternMatching() {
def email = "user@example.com"
def phoneNumber = "555-123-4567"
// Regex pattern matching
assertMatches(/\w+@\w+\.\w+/, email)
assertMatches(/\d{3}-\d{3}-\d{4}/, phoneNumber)
// Pattern with message
assertMatches("Invalid email format", /\w+@\w+\.\w+/, "user@example.com")
}
}Utilities for integration testing with external systems and resources.
Usage Examples:
import groovy.test.GroovyTestCase
class IntegrationTestExample extends GroovyTestCase {
void testDatabaseIntegration() {
// Setup test database
def sql = Sql.newInstance("jdbc:h2:mem:testdb", "sa", "", "org.h2.Driver")
try {
// Setup test data
sql.execute("""
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(50),
price DECIMAL(10,2)
)
""")
sql.execute("INSERT INTO products VALUES (1, 'Widget', 19.99)")
sql.execute("INSERT INTO products VALUES (2, 'Gadget', 29.99)")
// Test service with real database
def productService = new ProductService(sql)
def products = productService.getAllProducts()
assertEquals(2, products.size())
assertTrue(products.any { it.name == "Widget" })
// Test product creation
productService.createProduct("Doohickey", 39.99)
def allProducts = productService.getAllProducts()
assertEquals(3, allProducts.size())
} finally {
sql.close()
}
}
void testFileSystemOperations() {
// Create temporary test directory
def testDir = File.createTempDir("groovy-test", "")
try {
def fileService = new FileService(testDir.absolutePath)
// Test file creation
fileService.createFile("test.txt", "Hello World")
assertTrue(new File(testDir, "test.txt").exists())
// Test file reading
def content = fileService.readFile("test.txt")
assertEquals("Hello World", content)
// Test file listing
def files = fileService.listFiles()
assertEquals(1, files.size())
assertEquals("test.txt", files[0])
} finally {
// Cleanup test directory
testDir.deleteDir()
}
}
void testWebServiceIntegration() {
// Mock HTTP client for web service testing
def mockHttp = new MockFor(HttpClient)
mockHttp.demand.get { url ->
if (url.contains("/api/users/1")) {
return [status: 200, body: '{"id": 1, "name": "John Doe"}']
}
return [status: 404, body: '{"error": "Not found"}']
}
mockHttp.use { httpClient ->
def webService = new UserWebService(httpClient)
def user = webService.getUser(1)
assertEquals("John Doe", user.name)
shouldFail {
webService.getUser(999) // Should throw exception for 404
}
}
}
}/**
* Base test case class with Groovy enhancements
*/
abstract class GroovyTestCase extends TestCase {
/**
* Get the Groovy shell used for script evaluation
*/
GroovyShell getShell();
/**
* Set custom binding for shell
*/
void setShell(GroovyShell shell);
/**
* Evaluate Groovy script in test context
*/
Object evaluate(String script);
}
/**
* Test suite for organizing multiple tests
*/
class GroovyTestSuite extends TestSuite {
/**
* Add test class to the suite
*/
void addTestSuite(Class<? extends TestCase> testClass);
/**
* Get count of tests in suite
*/
int countTestCases();
/**
* Run all tests with custom result handler
*/
TestResult run(TestResult result);
}/**
* Mock object creator with call verification
*/
class MockFor {
/**
* The class being mocked
*/
Class getMockedClass();
/**
* Define expected method behavior
*/
void demand(String methodName, Closure implementation);
void demand(String methodName, int callCount, Closure implementation);
/**
* Create and use mock instance
*/
Object use(Closure closure);
/**
* Verify all expected calls were made
*/
void verify();
}
/**
* Stub object creator without call verification
*/
class StubFor {
/**
* The class being stubbed
*/
Class getStubbedClass();
/**
* Define method behavior
*/
void demand(String methodName, Closure implementation);
/**
* Create and use stub instance
*/
Object use(Closure closure);
}Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy