or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-transformations.mdindex.mdmock-stub.mdtest-cases.mdtest-suites.md

ast-transformations.mddocs/

0

# AST Transformations

1

2

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

3

4

## Capabilities

5

6

### @NotYetImplemented Annotation

7

8

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.

9

10

```groovy { .api }

11

/**

12

* Method annotation for inverting test case results

13

* Compatible with JUnit 3, 4, and 5

14

*/

15

@interface NotYetImplemented {

16

17

/**

18

* Custom exception class for unexpected passes

19

* Must have constructor accepting single String message

20

*/

21

Class<? extends AssertionError> exception() default AssertionError.class;

22

}

23

```

24

25

**Usage Examples:**

26

27

```groovy

28

import groovy.test.NotYetImplemented

29

import groovy.test.GroovyTestCase

30

31

class FeatureTest extends GroovyTestCase {

32

33

@NotYetImplemented

34

void testNewFeatureNotImplementedYet() {

35

// This test currently fails, which is expected

36

def result = myNewFeature()

37

assertEquals("expected behavior", result)

38

39

// When the feature is implemented, remove @NotYetImplemented

40

// If you forget, the test will fail to remind you

41

}

42

43

@NotYetImplemented(exception = MyCustomException)

44

void testWithCustomException() {

45

// If this test unexpectedly passes, throw MyCustomException

46

def result = anotherNewFeature()

47

assertTrue(result.isValid())

48

}

49

50

void testExistingFeature() {

51

// Normal test without annotation

52

def result = existingFeature()

53

assertNotNull(result)

54

}

55

}

56

```

57

58

**JUnit 4 Usage:**

59

60

```groovy

61

import groovy.test.NotYetImplemented

62

import org.junit.Test

63

import static groovy.test.GroovyAssert.*

64

65

class JUnit4FeatureTest {

66

67

@Test

68

@NotYetImplemented

69

void testNotImplementedFeature() {

70

// Currently fails - that's expected

71

def service = new FeatureService()

72

def result = service.newMethod()

73

assertEquals("expected", result)

74

}

75

76

@Test

77

void testImplementedFeature() {

78

// Normal passing test

79

def service = new FeatureService()

80

assertNotNull(service.existingMethod())

81

}

82

}

83

```

84

85

**JUnit 5 Usage:**

86

87

```groovy

88

import groovy.test.NotYetImplemented

89

import org.junit.jupiter.api.Test

90

import static org.junit.jupiter.api.Assertions.*

91

92

class JUnit5FeatureTest {

93

94

@Test

95

@NotYetImplemented

96

void testFutureFeature() {

97

// Implementation pending

98

def calculator = new Calculator()

99

assertEquals(42, calculator.complexCalculation())

100

}

101

}

102

```

103

104

### Legacy @NotYetImplemented (Deprecated)

105

106

The legacy annotation in `groovy.transform` package is deprecated. Use `groovy.test.NotYetImplemented` instead.

107

108

```groovy { .api }

109

/**

110

* Legacy annotation - use groovy.test.NotYetImplemented instead

111

* @deprecated use groovy.test.NotYetImplemented

112

*/

113

@Deprecated

114

@interface groovy.transform.NotYetImplemented {

115

// No attributes

116

}

117

```

118

119

## Behavior Details

120

121

### Test Inversion Logic

122

123

The `@NotYetImplemented` annotation works by:

124

125

1. **Catching test failures**: If the test method throws any exception, the annotation catches it and the test passes

126

2. **Detecting unexpected success**: If the test method completes without throwing an exception, the annotation throws an `AssertionError` (or custom exception)

127

3. **Preserving stack traces**: Original exception details are logged for debugging

128

129

### Exception Handling

130

131

```groovy

132

class NotYetImplementedTest extends GroovyTestCase {

133

134

@NotYetImplemented

135

void testCurrentlyFailing() {

136

// This assertion fails, so the test passes overall

137

assertEquals("not implemented", getFeatureResult())

138

}

139

140

@NotYetImplemented

141

void testUnexpectedlyPassing() {

142

// If this starts passing, you'll get an error like:

143

// "testUnexpectedlyPassing is marked as not yet implemented but passes unexpectedly"

144

assertTrue(true) // This would cause failure if feature is implemented

145

}

146

}

147

```

148

149

### Custom Exception Types

150

151

Use custom exceptions to distinguish between different types of not-yet-implemented features:

152

153

```groovy

154

class DatabaseFeatureException extends AssertionError {

155

DatabaseFeatureException(String message) {

156

super(message)

157

}

158

}

159

160

class ApiFeatureException extends AssertionError {

161

ApiFeatureException(String message) {

162

super(message)

163

}

164

}

165

166

class FeatureTest extends GroovyTestCase {

167

168

@NotYetImplemented(exception = DatabaseFeatureException)

169

void testDatabaseFeature() {

170

// Database-related feature not implemented

171

def result = database.advancedQuery()

172

assertNotNull(result)

173

}

174

175

@NotYetImplemented(exception = ApiFeatureException)

176

void testApiFeature() {

177

// API-related feature not implemented

178

def response = api.newEndpoint()

179

assertEquals(200, response.status)

180

}

181

}

182

```

183

184

## Integration with Test Suites

185

186

The annotation works seamlessly with all test suite types:

187

188

```groovy

189

// Works with GroovyTestSuite

190

java -Dtest=NotImplementedFeatureTest.groovy groovy.test.GroovyTestSuite

191

192

// Works with AllTestSuite

193

def suite = AllTestSuite.suite("test", "**/*Test.groovy")

194

// Tests with @NotYetImplemented will be included and behave correctly

195

```

196

197

## Development Workflow

198

199

Typical test-driven development workflow with `@NotYetImplemented`:

200

201

### 1. Write Failing Test

202

203

```groovy

204

class UserServiceTest extends GroovyTestCase {

205

206

@NotYetImplemented

207

void testUserPasswordReset() {

208

def service = new UserService()

209

def result = service.resetPassword("user@example.com")

210

211

assertTrue(result.success)

212

assertNotNull(result.resetToken)

213

assertEquals("user@example.com", result.email)

214

}

215

}

216

```

217

218

### 2. Run Tests (Should Pass)

219

220

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

221

222

### 3. Implement Feature

223

224

```groovy

225

class UserService {

226

def resetPassword(String email) {

227

// Implementation added

228

return [

229

success: true,

230

resetToken: generateToken(),

231

email: email

232

]

233

}

234

}

235

```

236

237

### 4. Remove Annotation

238

239

```groovy

240

class UserServiceTest extends GroovyTestCase {

241

242

// @NotYetImplemented - Remove this annotation

243

void testUserPasswordReset() {

244

def service = new UserService()

245

def result = service.resetPassword("user@example.com")

246

247

assertTrue(result.success)

248

assertNotNull(result.resetToken)

249

assertEquals("user@example.com", result.email)

250

}

251

}

252

```

253

254

### 5. Test Now Passes Normally

255

256

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

257

258

## Error Messages

259

260

Clear error messages help identify when features are implemented:

261

262

```

263

testUserRegistration is marked as not yet implemented but passes unexpectedly

264

```

265

266

This error tells you:

267

1. Which test method is affected

268

2. That the feature appears to be working

269

3. You should remove the `@NotYetImplemented` annotation

270

271

## Compatibility Notes

272

273

- **JUnit 3**: Full support with `GroovyTestCase`

274

- **JUnit 4**: Full support with `@Test` methods

275

- **JUnit 5**: Full support with `@Test` methods

276

- **Spock**: Works with Spock test methods

277

- **TestNG**: Compatible with TestNG test methods

278

279

The annotation uses compile-time AST transformation, so it works regardless of the test framework used at runtime.