or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdargument-capturing.mdbdd-testing.mdindex.mdjunit-integration.mdmatchers.mdmock-creation.mdstubbing.mdverification.md

bdd-testing.mddocs/

0

# BDD-Style Testing

1

2

BDDMockito provides Behavior Driven Development syntax for Mockito, using given/when/then structure that aligns with BDD testing practices. This makes tests more readable and follows natural language patterns.

3

4

## BDD Stubbing

5

6

### Given-Will Pattern

7

8

Replace `when().thenReturn()` with `given().willReturn()`:

9

10

```java { .api }

11

public static <T> BDDMyOngoingStubbing<T> given(T methodCall);

12

13

public interface BDDMyOngoingStubbing<T> {

14

BDDMyOngoingStubbing<T> willReturn(T value);

15

BDDMyOngoingStubbing<T> willReturn(T value, T... values);

16

BDDMyOngoingStubbing<T> willThrow(Throwable... throwables);

17

BDDMyOngoingStubbing<T> willThrow(Class<? extends Throwable> throwableType);

18

BDDMyOngoingStubbing<T> willAnswer(Answer<?> answer);

19

BDDMyOngoingStubbing<T> willCallRealMethod();

20

T getMock();

21

}

22

```

23

24

**Traditional Mockito vs BDD Style:**

25

26

```java

27

// Traditional Mockito style

28

@Test

29

public void shouldReturnUserWhenValidId() {

30

// Setup

31

when(userRepository.findById(123)).thenReturn(user);

32

33

// Execute

34

User result = userService.getUser(123);

35

36

// Verify

37

assertEquals(user, result);

38

}

39

40

// BDD style

41

@Test

42

public void shouldReturnUserWhenValidId() {

43

// Given

44

given(userRepository.findById(123)).willReturn(user);

45

46

// When

47

User result = userService.getUser(123);

48

49

// Then

50

assertEquals(user, result);

51

}

52

```

53

54

### BDD Stubbing Methods

55

56

```java { .api }

57

public static <T> BDDStubber willReturn(T value);

58

public static BDDStubber willThrow(Throwable... throwables);

59

public static BDDStubber willThrow(Class<? extends Throwable> throwableType);

60

public static BDDStubber willAnswer(Answer<?> answer);

61

public static BDDStubber willDoNothing();

62

public static BDDStubber willCallRealMethod();

63

```

64

65

**Usage Examples:**

66

67

```java

68

// Return values

69

given(calculator.add(2, 3)).willReturn(5);

70

given(userService.findByEmail("user@example.com")).willReturn(user);

71

72

// Multiple return values

73

given(randomService.nextInt()).willReturn(1, 2, 3);

74

75

// Throw exceptions

76

given(paymentService.processPayment(invalidCard)).willThrow(PaymentException.class);

77

given(databaseService.connect()).willThrow(new SQLException("Connection failed"));

78

79

// Custom answers

80

given(timeService.getCurrentTime()).willAnswer(invocation -> Instant.now());

81

82

// Call real method (for spies)

83

given(spy.calculate(anyInt())).willCallRealMethod();

84

```

85

86

## BDD Void Method Stubbing

87

88

### Will-Do Pattern for Void Methods

89

90

Use the will-do pattern for stubbing void methods:

91

92

```java

93

// Traditional style

94

doThrow(new RuntimeException()).when(emailService).sendEmail(anyString());

95

doNothing().when(auditService).log(anyString());

96

97

// BDD style

98

willThrow(new RuntimeException()).given(emailService).sendEmail(anyString());

99

willDoNothing().given(auditService).log(anyString());

100

```

101

102

**Complete Example:**

103

104

```java

105

@Test

106

public void shouldHandleEmailFailure() {

107

// Given

108

willThrow(EmailException.class).given(emailService).sendEmail(anyString());

109

110

// When

111

assertThrows(EmailException.class, () -> {

112

userService.registerUser("john@example.com", "John");

113

});

114

115

// Then

116

verify(auditService).logError(contains("Email sending failed"));

117

}

118

```

119

120

## BDD Verification

121

122

### Then-Should Pattern (v1.10.0+)

123

124

Use `then()` for BDD-style verification:

125

126

```java { .api }

127

public static <T> Then<T> then(T mock);

128

129

public interface Then<T> {

130

T should();

131

T should(VerificationMode mode);

132

T shouldHaveNoMoreInteractions();

133

T shouldHaveZeroInteractions();

134

}

135

```

136

137

**Usage Examples:**

138

139

```java

140

// Traditional verification

141

verify(emailService).sendEmail("user@example.com");

142

verify(auditService, times(2)).log(anyString());

143

verifyNoMoreInteractions(emailService);

144

145

// BDD verification

146

then(emailService).should().sendEmail("user@example.com");

147

then(auditService).should(times(2)).log(anyString());

148

then(emailService).shouldHaveNoMoreInteractions();

149

```

150

151

## Complete BDD Test Structure

152

153

### Full Given/When/Then Example

154

155

```java

156

@RunWith(MockitoJUnitRunner.class)

157

public class UserServiceBDDTest {

158

159

@Mock private UserRepository userRepository;

160

@Mock private EmailService emailService;

161

@Mock private AuditService auditService;

162

163

@InjectMocks private UserService userService;

164

165

@Test

166

public void shouldCreateUserAndSendWelcomeEmail() {

167

// Given

168

User newUser = new User("john@example.com", "John Doe");

169

given(userRepository.save(any(User.class))).willReturn(newUser);

170

given(emailService.isValidEmail("john@example.com")).willReturn(true);

171

172

// When

173

User result = userService.createUser("john@example.com", "John Doe");

174

175

// Then

176

then(result).isEqualTo(newUser);

177

then(userRepository).should().save(any(User.class));

178

then(emailService).should().sendWelcomeEmail("john@example.com");

179

then(auditService).should().logUserCreation(newUser.getId());

180

}

181

182

@Test

183

public void shouldThrowExceptionWhenEmailIsInvalid() {

184

// Given

185

given(emailService.isValidEmail("invalid-email")).willReturn(false);

186

187

// When/Then

188

assertThrows(InvalidEmailException.class, () -> {

189

userService.createUser("invalid-email", "John Doe");

190

});

191

192

then(userRepository).should(never()).save(any(User.class));

193

then(emailService).shouldHaveNoMoreInteractions();

194

}

195

}

196

```

197

198

### Complex BDD Scenario

199

200

```java

201

@Test

202

public void shouldProcessPaymentWithRetryLogic() {

203

// Given

204

PaymentRequest request = new PaymentRequest(100.00, "USD");

205

given(paymentGateway.processPayment(request))

206

.willThrow(TemporaryPaymentException.class) // First attempt fails

207

.willThrow(TemporaryPaymentException.class) // Second attempt fails

208

.willReturn(new PaymentResult("SUCCESS")); // Third attempt succeeds

209

210

// When

211

PaymentResult result = paymentService.processWithRetry(request);

212

213

// Then

214

then(result.getStatus()).isEqualTo("SUCCESS");

215

then(paymentGateway).should(times(3)).processPayment(request);

216

then(auditService).should().logPaymentRetry(request.getId(), 2);

217

then(notificationService).should().notifyPaymentSuccess(request.getId());

218

}

219

```

220

221

## BDD with AssertJ

222

223

Combine BDD Mockito with AssertJ for fluent assertions:

224

225

```java

226

import static org.assertj.core.api.BDDAssertions.then;

227

import static org.mockito.BDDMockito.*;

228

229

@Test

230

public void shouldCalculateOrderTotal() {

231

// Given

232

Order order = new Order();

233

order.addItem(new OrderItem("item1", 10.00));

234

order.addItem(new OrderItem("item2", 15.00));

235

236

given(taxService.calculateTax(25.00)).willReturn(2.50);

237

given(discountService.calculateDiscount(order)).willReturn(5.00);

238

239

// When

240

OrderTotal total = orderService.calculateTotal(order);

241

242

// Then

243

then(total.getSubtotal()).isEqualTo(25.00);

244

then(total.getTax()).isEqualTo(2.50);

245

then(total.getDiscount()).isEqualTo(5.00);

246

then(total.getTotal()).isEqualTo(22.50);

247

248

then(taxService).should().calculateTax(25.00);

249

then(discountService).should().calculateDiscount(order);

250

}

251

```

252

253

## BDD Best Practices

254

255

### Clear Test Structure

256

257

```java

258

@Test

259

public void shouldApproveAccountWhenValidDocuments() {

260

// Given - Set up the scenario

261

Account account = new Account("ACC123");

262

List<Document> validDocuments = Arrays.asList(

263

new Document("ID", "valid"),

264

new Document("Proof of Address", "valid")

265

);

266

given(documentValidator.validateAll(validDocuments)).willReturn(true);

267

given(riskAssessment.evaluate(account)).willReturn(RiskLevel.LOW);

268

269

// When - Execute the behavior

270

ApprovalResult result = accountService.approve(account, validDocuments);

271

272

// Then - Verify the outcome

273

then(result.isApproved()).isTrue();

274

then(result.getReason()).isEqualTo("All validations passed");

275

276

then(documentValidator).should().validateAll(validDocuments);

277

then(riskAssessment).should().evaluate(account);

278

then(notificationService).should().notifyApproval(account.getId());

279

}

280

```

281

282

### Descriptive Test Names

283

284

```java

285

// Good - describes behavior in business terms

286

@Test

287

public void shouldSendReminderEmailWhenSubscriptionExpiresInThreeDays() { }

288

289

@Test

290

public void shouldBlockTransactionWhenAmountExceedsDailyLimit() { }

291

292

@Test

293

public void shouldCreateAccountWithTrialPeriodForNewUsers() { }

294

295

// Avoid - technical implementation details

296

@Test

297

public void shouldCallEmailServiceSendMethod() { }

298

299

@Test

300

public void shouldReturnTrueFromValidateMethod() { }

301

```

302

303

### Scenario-Based Testing

304

305

```java

306

@Nested

307

@DisplayName("User Registration Scenarios")

308

class UserRegistrationScenarios {

309

310

@Test

311

@DisplayName("Given valid user data, when registering, then should create account and send welcome email")

312

public void validRegistration() {

313

// Given

314

UserRegistrationData userData = validUserData();

315

given(emailValidator.isValid(userData.getEmail())).willReturn(true);

316

given(userRepository.existsByEmail(userData.getEmail())).willReturn(false);

317

318

// When

319

RegistrationResult result = userService.register(userData);

320

321

// Then

322

then(result.isSuccessful()).isTrue();

323

then(emailService).should().sendWelcomeEmail(userData.getEmail());

324

}

325

326

@Test

327

@DisplayName("Given duplicate email, when registering, then should reject with appropriate error")

328

public void duplicateEmailRegistration() {

329

// Given

330

UserRegistrationData userData = validUserData();

331

given(userRepository.existsByEmail(userData.getEmail())).willReturn(true);

332

333

// When

334

RegistrationResult result = userService.register(userData);

335

336

// Then

337

then(result.isSuccessful()).isFalse();

338

then(result.getErrorMessage()).contains("Email already exists");

339

then(emailService).shouldHaveZeroInteractions();

340

}

341

}

342

```

343

344

### Integration with Cucumber-Style Comments

345

346

```java

347

@Test

348

public void shouldProcessRefundRequest() {

349

// Given: A customer has made a purchase and requests a refund within the allowed period

350

Purchase purchase = new Purchase("ORDER123", 99.99, LocalDate.now().minusDays(5));

351

RefundRequest request = new RefundRequest(purchase.getId(), "Product damaged");

352

353

given(purchaseRepository.findById("ORDER123")).willReturn(purchase);

354

given(refundPolicy.isEligible(purchase, request)).willReturn(true);

355

given(paymentGateway.processRefund(purchase.getAmount())).willReturn(refundResult("SUCCESS"));

356

357

// When: The customer service processes the refund request

358

RefundResult result = refundService.processRefund(request);

359

360

// Then: The refund should be approved and processed

361

then(result.isApproved()).isTrue();

362

then(result.getAmount()).isEqualTo(99.99);

363

364

// And: All relevant services should be called

365

then(paymentGateway).should().processRefund(99.99);

366

then(notificationService).should().notifyRefundApproval(request);

367

}

368

```