Groovy testing library providing JUnit-based testing utilities including test cases, assertions, and mock/stub frameworks
—
Annotation-based test enhancements including the @NotYetImplemented transformation for test-driven development. Automatically inverts test results to support "failing tests first" development patterns.
Method annotation that inverts test case results - tests fail when they pass and pass when they fail. Ideal for test-driven development where you write failing tests first.
/**
* Method annotation for inverting test case results
* Compatible with JUnit 3, 4, and 5
*/
@interface NotYetImplemented {
/**
* Custom exception class for unexpected passes
* Must have constructor accepting single String message
*/
Class<? extends AssertionError> exception() default AssertionError.class;
}Usage Examples:
import groovy.test.NotYetImplemented
import groovy.test.GroovyTestCase
class FeatureTest extends GroovyTestCase {
@NotYetImplemented
void testNewFeatureNotImplementedYet() {
// This test currently fails, which is expected
def result = myNewFeature()
assertEquals("expected behavior", result)
// When the feature is implemented, remove @NotYetImplemented
// If you forget, the test will fail to remind you
}
@NotYetImplemented(exception = MyCustomException)
void testWithCustomException() {
// If this test unexpectedly passes, throw MyCustomException
def result = anotherNewFeature()
assertTrue(result.isValid())
}
void testExistingFeature() {
// Normal test without annotation
def result = existingFeature()
assertNotNull(result)
}
}JUnit 4 Usage:
import groovy.test.NotYetImplemented
import org.junit.Test
import static groovy.test.GroovyAssert.*
class JUnit4FeatureTest {
@Test
@NotYetImplemented
void testNotImplementedFeature() {
// Currently fails - that's expected
def service = new FeatureService()
def result = service.newMethod()
assertEquals("expected", result)
}
@Test
void testImplementedFeature() {
// Normal passing test
def service = new FeatureService()
assertNotNull(service.existingMethod())
}
}JUnit 5 Usage:
import groovy.test.NotYetImplemented
import org.junit.jupiter.api.Test
import static org.junit.jupiter.api.Assertions.*
class JUnit5FeatureTest {
@Test
@NotYetImplemented
void testFutureFeature() {
// Implementation pending
def calculator = new Calculator()
assertEquals(42, calculator.complexCalculation())
}
}The legacy annotation in groovy.transform package is deprecated. Use groovy.test.NotYetImplemented instead.
/**
* Legacy annotation - use groovy.test.NotYetImplemented instead
* @deprecated use groovy.test.NotYetImplemented
*/
@Deprecated
@interface groovy.transform.NotYetImplemented {
// No attributes
}The @NotYetImplemented annotation works by:
AssertionError (or custom exception)class NotYetImplementedTest extends GroovyTestCase {
@NotYetImplemented
void testCurrentlyFailing() {
// This assertion fails, so the test passes overall
assertEquals("not implemented", getFeatureResult())
}
@NotYetImplemented
void testUnexpectedlyPassing() {
// If this starts passing, you'll get an error like:
// "testUnexpectedlyPassing is marked as not yet implemented but passes unexpectedly"
assertTrue(true) // This would cause failure if feature is implemented
}
}Use custom exceptions to distinguish between different types of not-yet-implemented features:
class DatabaseFeatureException extends AssertionError {
DatabaseFeatureException(String message) {
super(message)
}
}
class ApiFeatureException extends AssertionError {
ApiFeatureException(String message) {
super(message)
}
}
class FeatureTest extends GroovyTestCase {
@NotYetImplemented(exception = DatabaseFeatureException)
void testDatabaseFeature() {
// Database-related feature not implemented
def result = database.advancedQuery()
assertNotNull(result)
}
@NotYetImplemented(exception = ApiFeatureException)
void testApiFeature() {
// API-related feature not implemented
def response = api.newEndpoint()
assertEquals(200, response.status)
}
}The annotation works seamlessly with all test suite types:
// Works with GroovyTestSuite
java -Dtest=NotImplementedFeatureTest.groovy groovy.test.GroovyTestSuite
// Works with AllTestSuite
def suite = AllTestSuite.suite("test", "**/*Test.groovy")
// Tests with @NotYetImplemented will be included and behave correctlyTypical test-driven development workflow with @NotYetImplemented:
class UserServiceTest extends GroovyTestCase {
@NotYetImplemented
void testUserPasswordReset() {
def service = new UserService()
def result = service.resetPassword("user@example.com")
assertTrue(result.success)
assertNotNull(result.resetToken)
assertEquals("user@example.com", result.email)
}
}The test fails because resetPassword is not implemented, but @NotYetImplemented inverts this to a pass.
class UserService {
def resetPassword(String email) {
// Implementation added
return [
success: true,
resetToken: generateToken(),
email: email
]
}
}class UserServiceTest extends GroovyTestCase {
// @NotYetImplemented - Remove this annotation
void testUserPasswordReset() {
def service = new UserService()
def result = service.resetPassword("user@example.com")
assertTrue(result.success)
assertNotNull(result.resetToken)
assertEquals("user@example.com", result.email)
}
}The test runs normally and passes, confirming the implementation works correctly.
Clear error messages help identify when features are implemented:
testUserRegistration is marked as not yet implemented but passes unexpectedlyThis error tells you:
@NotYetImplemented annotationGroovyTestCase@Test methods@Test methodsThe annotation uses compile-time AST transformation, so it works regardless of the test framework used at runtime.
Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy-test