CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-codehaus-groovy--groovy-test

Groovy testing library providing JUnit-based testing utilities including test cases, assertions, and mock/stub frameworks

Pending
Overview
Eval results
Files

ast-transformations.mddocs/

AST Transformations

Annotation-based test enhancements including the @NotYetImplemented transformation for test-driven development. Automatically inverts test results to support "failing tests first" development patterns.

Capabilities

@NotYetImplemented Annotation

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())
    }
}

Legacy @NotYetImplemented (Deprecated)

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
}

Behavior Details

Test Inversion Logic

The @NotYetImplemented annotation works by:

  1. Catching test failures: If the test method throws any exception, the annotation catches it and the test passes
  2. Detecting unexpected success: If the test method completes without throwing an exception, the annotation throws an AssertionError (or custom exception)
  3. Preserving stack traces: Original exception details are logged for debugging

Exception Handling

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
    }
}

Custom Exception Types

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)
    }
}

Integration with Test Suites

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 correctly

Development Workflow

Typical test-driven development workflow with @NotYetImplemented:

1. Write Failing Test

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)
    }
}

2. Run Tests (Should Pass)

The test fails because resetPassword is not implemented, but @NotYetImplemented inverts this to a pass.

3. Implement Feature

class UserService {
    def resetPassword(String email) {
        // Implementation added
        return [
            success: true,
            resetToken: generateToken(),
            email: email
        ]
    }
}

4. Remove Annotation

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)
    }
}

5. Test Now Passes Normally

The test runs normally and passes, confirming the implementation works correctly.

Error Messages

Clear error messages help identify when features are implemented:

testUserRegistration is marked as not yet implemented but passes unexpectedly

This error tells you:

  1. Which test method is affected
  2. That the feature appears to be working
  3. You should remove the @NotYetImplemented annotation

Compatibility Notes

  • JUnit 3: Full support with GroovyTestCase
  • JUnit 4: Full support with @Test methods
  • JUnit 5: Full support with @Test methods
  • Spock: Works with Spock test methods
  • TestNG: Compatible with TestNG test methods

The 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

docs

ast-transformations.md

index.md

mock-stub.md

test-cases.md

test-suites.md

tile.json