Mockito Magic: A Journey through @Mock, @Spy, @Captor, and @InjectMocks in Spring Boot!
Dive into Mockito with Spring Boot! Discover how to use @Mock, @Spy, @Captor, and @InjectMocks to supercharge your Java unit testing skills.
Hello there, coders ! Today, we’re embarking on an exciting expedition into the realm of testing in Spring Boot with Mockito. We’ll be getting up close and personal with the annotations @Mock, @Spy, @Captor, and @InjectMocks. Buckle up, because we’re about to take off!
1. The Skinny on Mockito
First off, let’s get a little backstory on Mockito. It’s a mocking framework that tastes especially good when mixed with Java unit tests. And by ‘tastes good’, we mean it helps you write clean, developer-friendly tests that validate your code like a strict but fair high school teacher. Mockito can help you emulate and alter your program’s behavior during testing, which is pretty rad if you ask us!
2. Setting Up the Stage
Before we dive into the specifics, let’s make sure we have our setup in order. Assuming you have a Spring Boot project up and running, add the following dependencies to your Maven pom.xml file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
Or, if you’re a Gradle aficionado, pop this into your build.gradle file:
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core'
3. Annotation Explorations
Now that the stage is set, let’s take a tour of the four annotations we’re here to explore.
@Mock
The @Mock
annotation is the bread and butter of Mockito. It allows you to create and inject mocked instances. It's like creating a stunt double for your class. This double can mimic any action and return whatever you tell it to. Let's check out an example.
Assume you have a service:
public class GreetingService {
public String greet(String name) {
return "Hello, " + name;
}
}
And you want to mock it in your test:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class GreetingServiceTest {
@Mock
GreetingService greetingService;
@Test
public void testGreet() {
when(greetingService.greet(anyString())).thenReturn("Hello, Mockito");
assertEquals("Hello, Mockito", greetingService.greet("John Doe"));
}
}
@Spy
Next up is the @Spy
annotation. A spy is a mock's sneakier sibling. Instead of completely simulating a class or interface, a spy creates a wrapper around the real object. It behaves like the real thing until you tell it to act differently.
Here’s how you might use a spy:
@ExtendWith(MockitoExtension.class)
public class GreetingServiceSpyTest {
@Spy
GreetingService greetingService;
@Test
public void testGreet() {
when(greetingService.greet(anyString())).thenReturn("Hello, Mockito");
assertEquals("Hello, Mockito", greetingService.greet("John Doe"));
assertEquals("Hello, Jane Doe", greetingService.greet("Jane Doe"));
}
}
In this example, for “John Doe”, the spy returns “Hello, Mockito”. But for “Jane Doe”, it uses the real method and returns “Hello, Jane Doe”.
@Captor
The @Captor
annotation is like the detective in your Mockito toolbox. It captures argument values for further assertions. Suppose you want to check not only what a method returns, but also what it was called with. That's when @Captor comes into play.
@ExtendWith(MockitoExtension.class)
public class GreetingServiceCaptorTest {
@Mock
GreetingService greetingService;
@Captor
ArgumentCaptor<String> captor;
@Test
public void testGreet() {
greetingService.greet("Mockito");
verify(greetingService).greet(captor.capture());
String name = captor.getValue();
assertEquals("Mockito", name);
}
}
@InjectMocks
Finally, there’s the @InjectMocks
annotation. This annotation auto-injects mocked dependencies into the class being tested. Imagine you have a class that requires a bunch of services, and you want to test that class with those services mocked. That's what @InjectMocks is for.
public class WelcomeService {
private GreetingService greetingService;
public WelcomeService(GreetingService greetingService) {
this.greetingService = greetingService;
}
public String welcome(String name) {
return greetingService.greet(name) + ", welcome!";
}
}
@ExtendWith(MockitoExtension.class)
public class WelcomeServiceTest {
@Mock
GreetingService greetingService;
@InjectMocks
WelcomeService welcomeService;
@Test
public void testWelcome() {
when(greetingService.greet(anyString())).thenReturn("Hello, Mockito");
assertEquals("Hello, Mockito, welcome!", welcomeService.welcome("John Doe"));
}
}
In this example, the WelcomeService depends on GreetingService, and Mockito is smart enough to inject our mock GreetingService into WelcomeService when we annotate it with @InjectMocks.
4. Wrap It Up
And that’s it, folks! You’ve just earned your stripes in Mockito annotations. Remember, @Mock is your basic mock, @Spy is the real object in a disguise, @Captor is your argument detective, and @InjectMocks is your automatic dependency injector. Go out there and test like a champ!
Keep in mind that testing is an art as much as it is a science, and Mockito is just one of many paintbrushes at your disposal. With these new tools in your arsenal, may your journey towards mastering the art of testing be as fun as it is fruitful! Happy testing!
🔗 Connect with me on LinkedIn!
I hope you found this article helpful! If you’re interested in learning more and staying up-to-date with my latest insights and articles, don’t hesitate to connect with me on LinkedIn.
Let’s grow our networks, engage in meaningful discussions, and share our experiences in the world of software development and beyond. Looking forward to connecting with you! 😊