CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-mockito--mockito-core

Mockito mock objects library core API and implementation for comprehensive Java unit testing

Pending
Overview
Eval results
Files

bdd-testing.mddocs/

BDD-Style Testing

This section covers Behavior-Driven Development (BDD) style testing using Mockito's BDDMockito class, which provides given/when/then syntax for more readable tests.

BDD Fundamentals

BDD vs Traditional Mockito

BDD-style testing focuses on behavior specification using natural language patterns.

// Traditional Mockito
when(userService.findUser("john")).thenReturn(johnUser);
verify(userService).findUser("john");

// BDD-style with BDDMockito  
given(userService.findUser("john")).willReturn(johnUser);
then(userService).should().findUser("john");

Core BDD Imports

import static org.mockito.BDDMockito.*;
// This includes all BDD methods plus standard Mockito methods

Given-When-Then Structure

BDD Test Structure

Organize tests using the given/when/then pattern.

@Test
void shouldCreateUserWhenValidDataProvided() {
    // GIVEN - Set up test conditions
    User inputUser = new User("john", "john@example.com");
    User savedUser = new User("john", "john@example.com", 1L);
    
    given(userRepository.save(any(User.class))).willReturn(savedUser);
    given(emailService.isValidEmail(anyString())).willReturn(true);
    
    // WHEN - Execute the behavior being tested
    User result = userService.createUser(inputUser);
    
    // THEN - Verify the outcomes
    then(userRepository).should().save(inputUser);
    then(emailService).should().isValidEmail("john@example.com");
    assertThat(result.getId()).isEqualTo(1L);
}

BDD Stubbing

Given Methods

Configure mock behavior using given() instead of when().

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

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();
    BDDMyOngoingStubbing<T> willDoNothing();
    T getMock();
}

Usage Examples:

@Test
void shouldHandleUserCreationScenarios() {
    // GIVEN successful validation
    given(validationService.validateUser(any(User.class)))
        .willReturn(ValidationResult.SUCCESS);
    
    // GIVEN repository will save and return user with ID
    given(userRepository.save(any(User.class)))
        .willReturn(userWithId);
    
    // GIVEN email service will send welcome email
    given(emailService.sendWelcomeEmail(any(User.class)))
        .willReturn(true);
    
    // WHEN creating user
    CreateUserResult result = userService.createUser(newUserData);
    
    // THEN all collaborators should be called appropriately
    then(validationService).should().validateUser(any(User.class));
    then(userRepository).should().save(any(User.class));
    then(emailService).should().sendWelcomeEmail(any(User.class));
}

Will-Methods for Stubbing

BDD alternatives to doXxx() methods.

public static BDDStubber willReturn(Object toBeReturned)
public static BDDStubber willReturn(Object toBeReturned, Object... toBeReturnedNext)
public static BDDStubber willThrow(Throwable... toBeThrown)
public static BDDStubber willThrow(Class<? extends Throwable> throwableType)
public static BDDStubber willAnswer(Answer<?> answer)
public static BDDStubber willDoNothing()
public static BDDStubber willCallRealMethod()

interface BDDStubber {
    <T> T given(T mock);
}

Usage Examples:

@Test
void shouldHandleEmailServiceFailures() {
    // GIVEN email service will fail on first call, succeed on second
    willThrow(EmailException.class)
        .willReturn(true)
        .given(emailService).sendEmail(anyString());
    
    // GIVEN notification service will do nothing when called
    willDoNothing().given(notificationService).notifyAdmin(anyString());
    
    // WHEN processing with retry logic
    boolean result = emailProcessor.sendWithRetry("message");
    
    // THEN service should retry and eventually succeed
    then(emailService).should(times(2)).sendEmail("message");
    then(notificationService).should().notifyAdmin("Email failed on first attempt");
    assertTrue(result);
}

BDD Verification

Then-Should Pattern

Verify interactions using then().should() syntax.

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

interface Then<T> {
    BDDInOrder<T> should();
    BDDInOrder<T> should(VerificationMode mode);
    BDDInOrder<T> shouldHaveNoInteractions();
    BDDInOrder<T> shouldHaveNoMoreInteractions();
    BDDInOrder<T> shouldHaveZeroInteractions(); // Deprecated
}

Usage Examples:

@Test
void shouldVerifyUserServiceInteractions() {
    // GIVEN
    given(userRepository.existsByEmail(anyString())).willReturn(false);
    given(userRepository.save(any(User.class))).willReturn(savedUser);
    
    // WHEN
    userService.registerUser("john@example.com", "password");
    
    // THEN - verify specific interactions
    then(userRepository).should().existsByEmail("john@example.com");
    then(userRepository).should().save(any(User.class));
    then(emailService).should().sendWelcomeEmail(savedUser);
    
    // THEN - verify no unexpected interactions
    then(userRepository).shouldHaveNoMoreInteractions();
    then(emailService).shouldHaveNoMoreInteractions();
}

@Test
void shouldVerifyInteractionCounts() {
    // WHEN
    for (int i = 0; i < 3; i++) {
        cacheService.get("key" + i);
    }
    
    // THEN
    then(cacheService).should(times(3)).get(anyString());
    then(cacheService).should(never()).put(anyString(), any());
    then(cacheService).should(atLeast(1)).get(startsWith("key"));
}

BDD Ordered Verification

InOrder BDD Style

Verify interaction order using BDD syntax.

interface BDDInOrder<T> {
    T should();
    T should(VerificationMode mode);
    void shouldHaveNoMoreInteractions();
}

Usage Examples:

@Test
void shouldProcessPaymentInCorrectOrder() {
    // GIVEN
    given(paymentValidator.validate(any())).willReturn(true);
    given(paymentGateway.charge(any())).willReturn(successResult);
    given(orderService.updateStatus(any(), any())).willReturn(updatedOrder);
    
    // WHEN
    paymentProcessor.processPayment(payment);
    
    // THEN - verify order of operations
    InOrder inOrder = inOrder(paymentValidator, paymentGateway, orderService);
    then(paymentValidator).should(inOrder).validate(payment);
    then(paymentGateway).should(inOrder).charge(payment);
    then(orderService).should(inOrder).updateStatus(payment.getOrderId(), PAID);
}

Advanced BDD Patterns

Scenario-Based Testing

Structure complex scenarios with clear BDD language.

class OrderProcessingBDDTest {
    
    @Test
    void shouldCompleteOrderWhenPaymentSucceeds() {
        // GIVEN an order with valid items
        Order order = orderWithValidItems();
        given(inventoryService.checkAvailability(any())).willReturn(true);
        
        // AND payment processing will succeed
        given(paymentService.processPayment(any())).willReturn(paymentSuccess());
        
        // AND shipping service is available
        given(shippingService.scheduleDelivery(any())).willReturn(deliveryScheduled());
        
        // WHEN processing the order
        OrderResult result = orderProcessor.processOrder(order);
        
        // THEN inventory should be checked
        then(inventoryService).should().checkAvailability(order.getItems());
        
        // AND payment should be processed
        then(paymentService).should().processPayment(order.getPayment());
        
        // AND delivery should be scheduled
        then(shippingService).should().scheduleDelivery(order);
        
        // AND order should be completed successfully
        assertThat(result.getStatus()).isEqualTo(OrderStatus.COMPLETED);
        assertThat(result.getTrackingNumber()).isNotNull();
    }
    
    @Test
    void shouldFailOrderWhenPaymentFails() {
        // GIVEN an order with valid items
        Order order = orderWithValidItems();
        given(inventoryService.checkAvailability(any())).willReturn(true);
        
        // BUT payment processing will fail
        given(paymentService.processPayment(any()))
            .willThrow(new PaymentFailedException("Insufficient funds"));
        
        // WHEN processing the order
        OrderResult result = orderProcessor.processOrder(order);
        
        // THEN inventory should be checked
        then(inventoryService).should().checkAvailability(order.getItems());
        
        // AND payment should be attempted
        then(paymentService).should().processPayment(order.getPayment());
        
        // BUT shipping should not be attempted
        then(shippingService).should(never()).scheduleDelivery(any());
        
        // AND order should fail with appropriate status
        assertThat(result.getStatus()).isEqualTo(OrderStatus.PAYMENT_FAILED);
        assertThat(result.getError()).contains("Insufficient funds");
    }
}

Custom BDD Matchers

Create domain-specific matchers for more readable assertions.

class CustomBDDMatchers {
    
    public static ArgumentMatcher<User> validUser() {
        return user -> user != null && 
                      user.getEmail() != null && 
                      user.getName() != null &&
                      user.getName().length() > 0;
    }
    
    public static ArgumentMatcher<Order> orderWithAmount(BigDecimal expectedAmount) {
        return order -> order != null && 
                       order.getTotalAmount().equals(expectedAmount);
    }
    
    @Test
    void shouldUseCustomMatchers() {
        // GIVEN
        given(userRepository.save(argThat(validUser()))).willReturn(savedUser);
        
        // WHEN
        userService.createUser("john", "john@example.com");
        
        // THEN
        then(userRepository).should().save(argThat(validUser()));
        then(orderService).should().createOrder(argThat(orderWithAmount(new BigDecimal("99.99"))));
    }
}

BDD with Spies

Behavioral Verification with Spies

Use BDD syntax with spies for partial mocking.

@Test
void shouldCallRealMethodsSelectivelyWithSpy() {
    // GIVEN a spy that calls real methods by default
    UserService userServiceSpy = spy(new UserService(userRepository));
    
    // BUT will return mock data for findUser
    given(userServiceSpy.findUser(anyString())).willReturn(mockUser);
    
    // WHEN calling updateUser (which internally calls findUser)
    userServiceSpy.updateUser("john", updateData);
    
    // THEN both real and mocked methods should be called appropriately
    then(userServiceSpy).should().findUser("john"); // Mocked
    then(userServiceSpy).should().updateUser("john", updateData); // Real method
    then(userRepository).should().save(any(User.class)); // Real method called save
}

Install with Tessl CLI

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

docs

additional-answers.md

additional-matchers.md

advanced-features.md

annotations.md

argument-matching.md

bdd-testing.md

index.md

mock-creation.md

static-mocking.md

stubbing.md

verification.md

tile.json