CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-mockito--mockito-all

A comprehensive Java mocking framework that enables developers to create test doubles for unit testing.

Pending
Overview
Eval results
Files

bdd-testing.mddocs/

BDD-Style Testing

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.

BDD Stubbing

Given-Will Pattern

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

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

public interface BDDMyOngoingStubbing<T> {
    BDDMyOngoingStubbing<T> willReturn(T value);
    BDDMyOngoingStubbing<T> willReturn(T value, T... values);
    BDDMyOngoingStubbing<T> willThrow(Throwable... throwables);
    BDDMyOngoingStubbing<T> willThrow(Class<? extends Throwable> throwableType);
    BDDMyOngoingStubbing<T> willAnswer(Answer<?> answer);
    BDDMyOngoingStubbing<T> willCallRealMethod();
    T getMock();
}

Traditional Mockito vs BDD Style:

// Traditional Mockito style
@Test
public void shouldReturnUserWhenValidId() {
    // Setup
    when(userRepository.findById(123)).thenReturn(user);
    
    // Execute
    User result = userService.getUser(123);
    
    // Verify
    assertEquals(user, result);
}

// BDD style
@Test
public void shouldReturnUserWhenValidId() {
    // Given
    given(userRepository.findById(123)).willReturn(user);
    
    // When
    User result = userService.getUser(123);
    
    // Then
    assertEquals(user, result);
}

BDD Stubbing Methods

public static <T> BDDStubber willReturn(T value);
public static BDDStubber willThrow(Throwable... throwables);
public static BDDStubber willThrow(Class<? extends Throwable> throwableType);
public static BDDStubber willAnswer(Answer<?> answer);
public static BDDStubber willDoNothing();
public static BDDStubber willCallRealMethod();

Usage Examples:

// Return values
given(calculator.add(2, 3)).willReturn(5);
given(userService.findByEmail("user@example.com")).willReturn(user);

// Multiple return values
given(randomService.nextInt()).willReturn(1, 2, 3);

// Throw exceptions
given(paymentService.processPayment(invalidCard)).willThrow(PaymentException.class);
given(databaseService.connect()).willThrow(new SQLException("Connection failed"));

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

// Call real method (for spies)
given(spy.calculate(anyInt())).willCallRealMethod();

BDD Void Method Stubbing

Will-Do Pattern for Void Methods

Use the will-do pattern for stubbing void methods:

// Traditional style
doThrow(new RuntimeException()).when(emailService).sendEmail(anyString());
doNothing().when(auditService).log(anyString());

// BDD style
willThrow(new RuntimeException()).given(emailService).sendEmail(anyString());
willDoNothing().given(auditService).log(anyString());

Complete Example:

@Test
public void shouldHandleEmailFailure() {
    // Given
    willThrow(EmailException.class).given(emailService).sendEmail(anyString());
    
    // When
    assertThrows(EmailException.class, () -> {
        userService.registerUser("john@example.com", "John");
    });
    
    // Then
    verify(auditService).logError(contains("Email sending failed"));
}

BDD Verification

Then-Should Pattern (v1.10.0+)

Use then() for BDD-style verification:

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

public interface Then<T> {
    T should();
    T should(VerificationMode mode);
    T shouldHaveNoMoreInteractions();
    T shouldHaveZeroInteractions();
}

Usage Examples:

// Traditional verification
verify(emailService).sendEmail("user@example.com");
verify(auditService, times(2)).log(anyString());
verifyNoMoreInteractions(emailService);

// BDD verification
then(emailService).should().sendEmail("user@example.com");
then(auditService).should(times(2)).log(anyString());
then(emailService).shouldHaveNoMoreInteractions();

Complete BDD Test Structure

Full Given/When/Then Example

@RunWith(MockitoJUnitRunner.class)
public class UserServiceBDDTest {
    
    @Mock private UserRepository userRepository;
    @Mock private EmailService emailService;
    @Mock private AuditService auditService;
    
    @InjectMocks private UserService userService;
    
    @Test
    public void shouldCreateUserAndSendWelcomeEmail() {
        // Given
        User newUser = new User("john@example.com", "John Doe");
        given(userRepository.save(any(User.class))).willReturn(newUser);
        given(emailService.isValidEmail("john@example.com")).willReturn(true);
        
        // When
        User result = userService.createUser("john@example.com", "John Doe");
        
        // Then
        then(result).isEqualTo(newUser);
        then(userRepository).should().save(any(User.class));
        then(emailService).should().sendWelcomeEmail("john@example.com");
        then(auditService).should().logUserCreation(newUser.getId());
    }
    
    @Test
    public void shouldThrowExceptionWhenEmailIsInvalid() {
        // Given
        given(emailService.isValidEmail("invalid-email")).willReturn(false);
        
        // When/Then
        assertThrows(InvalidEmailException.class, () -> {
            userService.createUser("invalid-email", "John Doe");
        });
        
        then(userRepository).should(never()).save(any(User.class));
        then(emailService).shouldHaveNoMoreInteractions();
    }
}

Complex BDD Scenario

@Test
public void shouldProcessPaymentWithRetryLogic() {
    // Given
    PaymentRequest request = new PaymentRequest(100.00, "USD");
    given(paymentGateway.processPayment(request))
        .willThrow(TemporaryPaymentException.class)  // First attempt fails
        .willThrow(TemporaryPaymentException.class)  // Second attempt fails
        .willReturn(new PaymentResult("SUCCESS"));   // Third attempt succeeds
    
    // When
    PaymentResult result = paymentService.processWithRetry(request);
    
    // Then
    then(result.getStatus()).isEqualTo("SUCCESS");
    then(paymentGateway).should(times(3)).processPayment(request);
    then(auditService).should().logPaymentRetry(request.getId(), 2);
    then(notificationService).should().notifyPaymentSuccess(request.getId());
}

BDD with AssertJ

Combine BDD Mockito with AssertJ for fluent assertions:

import static org.assertj.core.api.BDDAssertions.then;
import static org.mockito.BDDMockito.*;

@Test
public void shouldCalculateOrderTotal() {
    // Given
    Order order = new Order();
    order.addItem(new OrderItem("item1", 10.00));
    order.addItem(new OrderItem("item2", 15.00));
    
    given(taxService.calculateTax(25.00)).willReturn(2.50);
    given(discountService.calculateDiscount(order)).willReturn(5.00);
    
    // When
    OrderTotal total = orderService.calculateTotal(order);
    
    // Then
    then(total.getSubtotal()).isEqualTo(25.00);
    then(total.getTax()).isEqualTo(2.50);
    then(total.getDiscount()).isEqualTo(5.00);
    then(total.getTotal()).isEqualTo(22.50);
    
    then(taxService).should().calculateTax(25.00);
    then(discountService).should().calculateDiscount(order);
}

BDD Best Practices

Clear Test Structure

@Test
public void shouldApproveAccountWhenValidDocuments() {
    // Given - Set up the scenario
    Account account = new Account("ACC123");
    List<Document> validDocuments = Arrays.asList(
        new Document("ID", "valid"),
        new Document("Proof of Address", "valid")
    );
    given(documentValidator.validateAll(validDocuments)).willReturn(true);
    given(riskAssessment.evaluate(account)).willReturn(RiskLevel.LOW);
    
    // When - Execute the behavior
    ApprovalResult result = accountService.approve(account, validDocuments);
    
    // Then - Verify the outcome
    then(result.isApproved()).isTrue();
    then(result.getReason()).isEqualTo("All validations passed");
    
    then(documentValidator).should().validateAll(validDocuments);
    then(riskAssessment).should().evaluate(account);
    then(notificationService).should().notifyApproval(account.getId());
}

Descriptive Test Names

// Good - describes behavior in business terms
@Test
public void shouldSendReminderEmailWhenSubscriptionExpiresInThreeDays() { }

@Test
public void shouldBlockTransactionWhenAmountExceedsDailyLimit() { }

@Test
public void shouldCreateAccountWithTrialPeriodForNewUsers() { }

// Avoid - technical implementation details
@Test
public void shouldCallEmailServiceSendMethod() { }

@Test
public void shouldReturnTrueFromValidateMethod() { }

Scenario-Based Testing

@Nested
@DisplayName("User Registration Scenarios")
class UserRegistrationScenarios {
    
    @Test
    @DisplayName("Given valid user data, when registering, then should create account and send welcome email")
    public void validRegistration() {
        // Given
        UserRegistrationData userData = validUserData();
        given(emailValidator.isValid(userData.getEmail())).willReturn(true);
        given(userRepository.existsByEmail(userData.getEmail())).willReturn(false);
        
        // When
        RegistrationResult result = userService.register(userData);
        
        // Then
        then(result.isSuccessful()).isTrue();
        then(emailService).should().sendWelcomeEmail(userData.getEmail());
    }
    
    @Test
    @DisplayName("Given duplicate email, when registering, then should reject with appropriate error")
    public void duplicateEmailRegistration() {
        // Given
        UserRegistrationData userData = validUserData();
        given(userRepository.existsByEmail(userData.getEmail())).willReturn(true);
        
        // When
        RegistrationResult result = userService.register(userData);
        
        // Then
        then(result.isSuccessful()).isFalse();
        then(result.getErrorMessage()).contains("Email already exists");
        then(emailService).shouldHaveZeroInteractions();
    }
}

Integration with Cucumber-Style Comments

@Test
public void shouldProcessRefundRequest() {
    // Given: A customer has made a purchase and requests a refund within the allowed period
    Purchase purchase = new Purchase("ORDER123", 99.99, LocalDate.now().minusDays(5));
    RefundRequest request = new RefundRequest(purchase.getId(), "Product damaged");
    
    given(purchaseRepository.findById("ORDER123")).willReturn(purchase);
    given(refundPolicy.isEligible(purchase, request)).willReturn(true);
    given(paymentGateway.processRefund(purchase.getAmount())).willReturn(refundResult("SUCCESS"));
    
    // When: The customer service processes the refund request
    RefundResult result = refundService.processRefund(request);
    
    // Then: The refund should be approved and processed
    then(result.isApproved()).isTrue();
    then(result.getAmount()).isEqualTo(99.99);
    
    // And: All relevant services should be called
    then(paymentGateway).should().processRefund(99.99);
    then(notificationService).should().notifyRefundApproval(request);
}

Install with Tessl CLI

npx tessl i tessl/maven-org-mockito--mockito-all

docs

annotations.md

argument-capturing.md

bdd-testing.md

index.md

junit-integration.md

matchers.md

mock-creation.md

stubbing.md

verification.md

tile.json