0
0
JUnittesting~15 mins

Argument captors in JUnit - Build an Automation Script

Choose your learning style9 modes available
Verify method argument using ArgumentCaptor in JUnit
Preconditions (2)
Step 1: Mock the repository dependency
Step 2: Call the service method that internally calls the repository method
Step 3: Capture the argument passed to the repository method using ArgumentCaptor
Step 4: Verify that the repository method was called once
Step 5: Assert that the captured argument matches the expected value
✅ Expected Result: The test passes confirming the repository method was called with the correct argument
Automation Requirements - JUnit 5 with Mockito
Assertions Needed:
Verify repository method call count
Assert captured argument value
Best Practices:
Use @ExtendWith(MockitoExtension.class) for Mockito support
Use ArgumentCaptor to capture method arguments
Use verify() to check method invocation
Keep test methods focused and clear
Automated Solution
JUnit
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    UserRepository userRepository;

    @InjectMocks
    UserService userService;

    @Test
    void testCreateUser_callsRepositoryWithCorrectUser() {
        // Arrange
        User expectedUser = new User("Alice", "alice@example.com");

        // Act
        userService.createUser("Alice", "alice@example.com");

        // Assert
        ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
        verify(userRepository, times(1)).save(userCaptor.capture());
        User capturedUser = userCaptor.getValue();

        assertEquals(expectedUser.getName(), capturedUser.getName());
        assertEquals(expectedUser.getEmail(), capturedUser.getEmail());
    }
}

// Supporting classes
class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() { return name; }
    public String getEmail() { return email; }
}

interface UserRepository {
    void save(User user);
}

class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(String name, String email) {
        User user = new User(name, email);
        userRepository.save(user);
    }
}

This test uses Mockito with JUnit 5 to verify that the UserService calls the save method of UserRepository with the correct User object.

We use @ExtendWith(MockitoExtension.class) to enable Mockito annotations.

The @Mock annotation creates a mock UserRepository. The @InjectMocks annotation creates a UserService instance and injects the mock repository.

In the test method, we call createUser on the service. Then we use ArgumentCaptor to capture the User object passed to save.

We verify that save was called exactly once. Then we assert that the captured User has the expected name and email.

This approach helps us check the exact argument passed to a mocked method, which is useful when the argument is created inside the method under test.

Common Mistakes - 4 Pitfalls
Not using ArgumentCaptor and instead verifying only method call count
Using raw Mockito verify without specifying times(1)
Not initializing mocks with MockitoExtension or MockitoAnnotations
{'mistake': 'Capturing argument but not asserting its values', 'why_bad': 'Capturing alone does not verify correctness; assertions are needed to confirm expected behavior.', 'correct_approach': "After capturing, assert the argument's fields or properties."}
Bonus Challenge

Now add data-driven testing with 3 different user inputs to verify the repository is called correctly each time

Show Hint