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

mock-stub.mddocs/

0

# Mock and Stub Framework

1

2

Comprehensive mocking framework supporting both strict (ordered) and loose (unordered) expectations. Uses metaclass interception to mock final classes and methods, with support for constructor interception and partial mocking.

3

4

## Capabilities

5

6

### MockFor (Strict Mocking)

7

8

Strict mock framework where method calls must occur in the exact order specified in the demand specification.

9

10

```groovy { .api }

11

/**

12

* Strict mock framework with ordered expectations

13

*/

14

class MockFor {

15

16

/** Create mock for class with optional constructor interception */

17

MockFor(Class clazz, boolean interceptConstruction = false);

18

19

/** Apply mock behavior within closure and verify expectations */

20

void use(Closure closure);

21

void use(GroovyObject obj, Closure closure);

22

23

/** Manual verification for instance-style mocking */

24

void verify(GroovyObject obj);

25

26

/** Ignore method calls matching filter pattern */

27

def ignore(Object filter, Closure filterBehavior = null);

28

29

/** Create proxy instance for instance-style mocking */

30

GroovyObject proxyInstance(Object args = null);

31

GroovyObject proxyDelegateInstance(Object args = null);

32

33

/** Demand object for recording expectations */

34

Demand demand;

35

36

/** Ignore object for specifying ignored methods */

37

Ignore ignore;

38

}

39

```

40

41

**Usage Examples:**

42

43

```groovy

44

import groovy.mock.interceptor.MockFor

45

46

class DatabaseServiceTest extends GroovyTestCase {

47

48

void testStrictMocking() {

49

def mock = new MockFor(Database)

50

51

// Define strict expectations (order matters)

52

mock.demand.connect { "connection-123" }

53

mock.demand.findUser { id -> [id: id, name: "User $id"] }

54

mock.demand.disconnect { }

55

56

mock.use {

57

def database = new Database()

58

def service = new DatabaseService(database)

59

60

def user = service.getUser(42)

61

assertEquals("User 42", user.name)

62

}

63

// Verification happens automatically

64

}

65

66

void testConstructorMocking() {

67

def mock = new MockFor(Person, true) // Enable constructor interception

68

def dummyPerson = new Person(first: "John", last: "Doe")

69

70

mock.demand.with {

71

Person() { dummyPerson } // Mock constructor

72

getFirst() { "Jane" } // Mock method calls

73

getLast() { "Smith" }

74

}

75

76

mock.use {

77

def person = new Person(first: "Original", last: "Name")

78

assertEquals("Jane", person.first)

79

assertEquals("Smith", person.last)

80

}

81

}

82

83

void testInstanceStyleMocking() {

84

def mock = new MockFor(Calculator)

85

mock.demand.add { a, b -> a + b + 1 } // Add 1 to normal behavior

86

87

def calc1 = mock.proxyInstance()

88

def calc2 = mock.proxyInstance()

89

90

assertEquals(8, calc1.add(3, 4)) // 3 + 4 + 1

91

assertEquals(16, calc2.add(7, 8)) // 7 + 8 + 1

92

93

[calc1, calc2].each { mock.verify(it) }

94

}

95

}

96

```

97

98

### StubFor (Loose Stubbing)

99

100

Loose stub framework where method calls can occur in any order and verification is optional.

101

102

```groovy { .api }

103

/**

104

* Loose stub framework with unordered expectations

105

*/

106

class StubFor {

107

108

/** Create stub for class with optional constructor interception */

109

StubFor(Class clazz, boolean interceptConstruction = false);

110

111

/** Apply stub behavior within closure */

112

void use(Closure closure);

113

void use(GroovyObject obj, Closure closure);

114

115

/** Manual verification (optional) */

116

void verify(GroovyObject obj);

117

void verify();

118

119

/** Ignore method calls matching filter pattern */

120

def ignore(Object filter, Closure filterBehavior = null);

121

122

/** Create proxy instance for instance-style stubbing */

123

GroovyObject proxyInstance(Object args = null);

124

GroovyObject proxyDelegateInstance(Object args = null);

125

126

/** Demand object for recording expectations */

127

Demand demand;

128

129

/** Ignore object for specifying ignored methods */

130

Ignore ignore;

131

}

132

```

133

134

**Usage Examples:**

135

136

```groovy

137

import groovy.mock.interceptor.StubFor

138

139

class WebServiceTest extends GroovyTestCase {

140

141

void testLooseStubbing() {

142

def stub = new StubFor(HttpClient)

143

144

// Define expectations (order doesn't matter)

145

stub.demand.with {

146

get { url -> "Response for $url" }

147

post { url, data -> "Posted $data to $url" }

148

setTimeout { timeout -> /* ignored */ }

149

}

150

151

stub.use {

152

def client = new HttpClient()

153

def service = new WebService(client)

154

155

// Calls can happen in any order

156

service.configure(5000) // setTimeout

157

def result1 = service.fetch("/users") // get

158

def result2 = service.create("/users", "data") // post

159

160

assertEquals("Response for /users", result1)

161

assertEquals("Posted data to /users", result2)

162

}

163

164

// Optional verification

165

stub.expect.verify()

166

}

167

168

void testRangeExpectations() {

169

def stub = new StubFor(Logger)

170

171

// Method can be called 0-3 times

172

stub.demand.log(0..3) { message ->

173

println "Logged: $message"

174

}

175

176

// Method must be called exactly 2 times

177

stub.demand.flush(2) {

178

println "Flushed"

179

}

180

181

stub.use {

182

def logger = new Logger()

183

def service = new BusinessService(logger)

184

185

service.processData() // May log 0-3 times, flushes twice

186

}

187

188

stub.verify()

189

}

190

}

191

```

192

193

### Demand Specification

194

195

Records method call expectations with cardinality and behavior specifications.

196

197

```groovy { .api }

198

/**

199

* Records method expectations and verifies call counts

200

*/

201

class Demand {

202

203

/** List of recorded call specifications */

204

List recorded;

205

206

/** Map of ignored method patterns */

207

Map ignore;

208

209

/** Dynamic method recording (called via demand.methodName syntax) */

210

Object invokeMethod(String methodName, Object args);

211

212

/** Verify that call counts match expectations */

213

void verify(List calls);

214

}

215

216

/**

217

* Individual call specification

218

*/

219

class CallSpec {

220

String name; // Method name

221

Closure behavior; // Method behavior closure

222

Range range; // Expected call count range

223

}

224

```

225

226

**Usage Examples:**

227

228

```groovy

229

void testDemandSpecification() {

230

def mock = new MockFor(Service)

231

232

// Different ways to specify call counts

233

mock.demand.simpleCall { "result" } // Exactly once (default)

234

mock.demand.optionalCall(0..1) { "maybe" } // 0 or 1 times

235

mock.demand.repeatedCall(3) { "repeated" } // Exactly 3 times

236

mock.demand.rangeCall(2..4) { "flexible" } // 2 to 4 times

237

238

// Method parameters and return values

239

mock.demand.calculate { a, b -> a * b }

240

mock.demand.process { data ->

241

assert data.size() > 0

242

return data.collect { it.toUpperCase() }

243

}

244

245

mock.use {

246

def service = new Service()

247

// Use service methods matching demand specification

248

}

249

}

250

```

251

252

### Ignore Specifications

253

254

Allows selective ignoring of method calls for partial mocking scenarios.

255

256

```groovy { .api }

257

/**

258

* Handles method call ignoring via dynamic method calls

259

*/

260

class Ignore {

261

// Dynamic method handling for ignore specifications

262

}

263

```

264

265

**Usage Examples:**

266

267

```groovy

268

void testIgnoreSpecifications() {

269

def mock = new MockFor(ComplexService)

270

271

// Ignore getter methods (fall through to real implementation)

272

mock.ignore(~/get.*/)

273

274

// Ignore specific method with custom behavior

275

mock.ignore('toString') { 'Mocked toString' }

276

277

// Ignore multiple patterns

278

mock.ignore(['equals', 'hashCode'])

279

280

// Use convenience syntax

281

mock.ignore.someMethod() // Equivalent to mock.ignore('someMethod')

282

283

mock.demand.importantMethod { "mocked result" }

284

285

mock.use {

286

def service = new ComplexService()

287

288

// Ignored methods work normally

289

def value = service.getValue() // Real implementation

290

def string = service.toString() // Returns 'Mocked toString'

291

292

// Demanded methods are intercepted

293

def result = service.importantMethod() // Returns 'mocked result'

294

}

295

}

296

```

297

298

## Advanced Features

299

300

### Constructor Interception

301

302

Enable constructor mocking for complete object lifecycle control:

303

304

```groovy

305

void testConstructorInterception() {

306

def mock = new MockFor(DatabaseConnection, true)

307

def mockConnection = new MockConnection()

308

309

mock.demand.DatabaseConnection { mockConnection }

310

mock.demand.execute { sql -> "Mock result for: $sql" }

311

312

mock.use {

313

def connection = new DatabaseConnection("jdbc:mock://localhost")

314

def result = connection.execute("SELECT * FROM users")

315

assertEquals("Mock result for: SELECT * FROM users", result)

316

}

317

}

318

```

319

320

### Proxy Instances for Java Integration

321

322

Use proxy instances when working with Java code that cannot use Groovy's `use` blocks:

323

324

```groovy

325

void testProxyInstances() {

326

def mock = new MockFor(PaymentService)

327

mock.demand.processPayment { amount -> "Payment of $amount processed" }

328

329

def paymentService = mock.proxyInstance()

330

def orderService = new OrderService(paymentService) // Java class

331

332

def result = orderService.completeOrder(100.0)

333

mock.verify(paymentService)

334

}

335

```

336

337

## Error Handling

338

339

Mock and stub failures throw `AssertionFailedError` with detailed messages:

340

341

- Call count mismatches show expected vs actual counts

342

- Unexpected method calls show method name and parameters

343

- Missing method calls show which expected calls never occurred

344

345

```groovy

346

// This will fail with detailed error message

347

mock.demand.calculate(2) { a, b -> a + b }

348

mock.use {

349

def service = new Service()

350

service.calculate(1, 2) // Called only once, expected twice

351

}

352

// AssertionFailedError: verify[0]: expected 2..2 call(s) to 'calculate' but was called 1 time(s).

353

```