Eclipse Mockito debugging; Doesn't stop on breakpoints in spied beans - java

I have a problem using Eclipse to debug Mockito based unittests. Tests are working fine but I cannot step in to spied code or stop on any breakpoint. Debugger is working fine in IntelliJ, but not in Eclipse.
I put a simple unittest to demonstrate my problem. I have JUnit test for SomeService which calls AnotherService. I'm using Mockito to mock output of AnotherService
SomeService.java
package com.example.my;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class SomeService {
#Autowired
private AnotherService anotherService;
public void callBoom() {
System.out.print(anotherService.boom());
}
}
AnotherClass.java
package com.example.my;
import org.springframework.stereotype.Service;
#Service
public class AnotherService {
public String boom() {
return "boom";
}
}
SomeServiceTest.java
package com.example.my;
import static org.mockito.Mockito.doReturn;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
class SomeServiceTest {
#Spy
#InjectMocks
private SomeService someService;
#Mock
private AnotherService anotherService;
#BeforeEach
void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("BOOM!!!").when(anotherService).boom();
}
#Test
void testBoom() {
someService.callBoom();
}
}
I can see "BOOM!!!" printed, so mock is working. But if I put breakpoint into callBoom, Eclipse never stop on it. For other tests it doesn't stop at any method of "spied" beans. When you have larger unittest inability to debug it is very frustrating. I'm running Eclipse 2019-09 with JDK 1.8.0.221, but tried it on 2020-03 as well.

Related

NullPointer Mocking Kafka Results

I am writing tests and I was looking to mock the result of the kafka admin client, when a topic is created.
I am using Mockito to write my unit tests.
Here is the test code:
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.common.KafkaFuture;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Mock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.main.Launch;
import io.restassured.RestAssured;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
#QuarkusTest
public class AppTest {
#InjectMocks
private App mockApp;
#Mock
private Client mockClient;
#Mock
private Database mockDatabase;
#Mock
private Admin mockKafkaAdmin;
#Mock
private ListTopicsResult mockListTopicResult;
#Mock
private KafkaFuture<Set<String>> mockKafkaFuture;
#Mock
private KafkaFuture<Void> mockKafkaFutureVoid;
#Mock
private Set<String> mockSet;
#Mock
private CreateTopicsResult mockCreateTopicResult;
#Mock
private Map<String, KafkaFuture<Void>> mockKafkaTopicResult;
#BeforeAll
public static void setupAll() {
RestAssured.filters(new RequestLoggingFilter(), new ResponseLoggingFilter());
}
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
#Launch(value = {}, exitCode = 1)
public void testLaunchCommandFailed() {}
#Test
public void testCreateTopic() throws InterruptedException, ExecutionException {
Mockito.when(mockClient.getKafka()).thenReturn(mockKafkaAdmin);
Mockito.when(mockClient.getKafka().createTopics(Mockito.anyList())).thenReturn(mockCreateTopicResult);
Mockito.when(mockCreateTopicResult.values()).thenReturn(mockKafkaTopicResult);
Mockito.when(mockKafkaTopicResult.get("meme.transmit.test")).thenReturn(mockKafkaFutureVoid);
mockApp.createTopic("test");
Mockito.when(mockClient.getKafka().listTopics()).thenReturn(mockListTopicResult);
Mockito.when(mockClient.getKafka().listTopics().names()).thenReturn(mockKafkaFuture);
Mockito.when(mockClient.getKafka().listTopics().names().get()).thenReturn(mockSet);
Assertions.assertTrue(mockApp.containsTopic("test"));
}
// ...
}
I get a nullpointer error when this line is called in the production code:
Mockito.when(mockCreateTopicResult.values()).thenReturn(mockKafkaTopicResult);
But as you can see I mocked it with mockKafkaTopicResult. What might I be missing here? Also is there an easier method to work with KafkaAdminClient when writing unit tests?
Mockito.when(mockClient.getKafka().createTopics(Mockito.anyList()))
.thenReturn(mockCreateTopicResult);
The issue was that I was using anyList(), when the createTopics() only accepts Collections.
The fix: Mockito.anyCollections()

Spring Boot Unit Test returns 404 instead of 200

I am new in springboot. I am just watching the Spring in Action and programming follow the author.
then things get difficult when i just reading the chapter 1. I need to test a controller. the code in this book is:
package tacos;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#RunWith(SpringRunner.class)
#WebMvcTest(HomeController.class) // <1>
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc; // <2>
#Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) // <3>
.andExpect(status().isOk()) // <4>
.andExpect(view().name("home")) // <5>
.andExpect(content().string( // <6>
containsString("Welcome to...")));
}
}
I am using springboot 2.7.3 so i just remove #RunWith(SpringRunner.class) from my code. and i immediately get an error when do the test:
after google that i realized it might not find my controller so I add #Import(HomeController.class) to the test class. It looks like:
package com.qph.tacos;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#WebMvcTest(HomeControllerTest.class)
#Import(HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testHomePage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("home"))
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("Welcome to...")));
}
}
then it just pass the test!!!
now i just wonder why the code written by the author can pass the test without #Import(HomeController.class)?
my directory structure is like:
the controller to be tested is:
package com.qph.tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/")
public String home() {
// return the name of template
return "home";
}
}
I am new in springboot. maybe it's a simple question but i really spent so much time on it.
Thanks for your help!
Change to
#WebMvcTest(HomeController.class)
You should reference the controller under test with this annotation, otherwise it won't be loaded into the application context.

NullPointerException in injectedMock

I have a RestController that I want to test:
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PetController implements PetApi {
#Autowired
PetRepository pr;
#Override
public ResponseEntity<Pet> addPet(#Valid Pet pet) {
pr.save(new PetBE(9L, "dummy"));
return new ResponseEntity<Pet>(pet, HttpStatus.OK);
}
}
import org.springframework.data.repository.CrudRepository;
public interface PetRepository extends CrudRepository<PetBE, Long> {
}
I want to mock PetRepository and test if the object passed is the object returned:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#SpringBootTest
public class PetControllerTest {
#InjectMocks
private PetController petController;
#MockBean
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
/*L35*/ ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
When I run this method as a gradle test, I get
com.example.petstore.backend.api.implementation.PetControllerTest > testAddPet() FAILED
java.lang.NullPointerException at PetControllerTest.java:35
which is petController.addPet(p1);.
My printlns in addPet are not displayed and I can't set any breakpoints there because it is mocked. The only reference in addPet that could be null is pr, but it works fine when I send a request with curl.
I've also tried adding
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
because it was suggested here but that gave an InitializationException:
com.example.petstore.backend.api.implementation.PetControllerTest > initializationError FAILED
org.junit.platform.commons.JUnitException at LifecycleMethodUtils.java:57
How can I debug this?
How can I get this to work?
You're mixing annotations from various testing frameworks here. If you wish to use the Mockito annotation #InjectMocks then I'd recommend not using any Spring-related mocking annotations at all, but rather the #Mock annotation to create a mocked version of the bean you want to inject (into the #InjectMocks-annotated field). Also make sure you bootstrap the Mockito extension with #ExtendWith(MockitoExtension.class). Something like:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#ExtendWith(MockitoExtension.class)
public class PetControllerTest {
#InjectMocks
private PetController petController;
#Mock
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
EDIT: Calling MockitoAnnotations.initMocks(this) inside for example a #BeforeEach-annotated method is necessary if you don't want to use the MockitoExtension. They're essentially the same thing, but it's less necessary in JUnit Jupiter because you can extend a test class with multiple extensions, which was not possible in JUnit 4.x. So if you wanted to bootstrap your test with both a Spring context and Mockito, then you had to pick one of them and setup the other one yourself.

Why Mockito with #MockBean is returning unexpected Pass when running JUnit test

Im trying get my head around how to create a MockBean to use in connection with Mockito. In my case I have two separate classes, one which is the #Controller in which Im trying to mock in another #SpringBootTest Junit test class.
But whenever Im running the Mockito as a Junit test case I am getting unexpected result which is pass when it should really return false ?
Am I missing some kind of configuration here?
I have attached source code for both of the classes to show you:
First is the controller class:
package com.springboot.test.testapplication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class TestController {
#RequestMapping("/welcome")
public String welcome(Model map) {
map.addAttribute("welcomeMessage", "welcome");
map.addAttribute("message", "important message");
return "welcome";
}
//Method I'm trying to get access to
public String return100() {
return "100";
}
}
and this is the Test class:
package com.springboot.test.testapplication;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
#SpringBootTest
class TestapplicationApplicationTests {
#MockBean
private TestController controller;
#Test
void contextLoads() {
Mockito.when(controller.return100()).thenReturn("2");
}
}
Any tips would be much appreciated..

JUnit Mockito with H2 Database

I am trying to write JUnit for spring boot controller using Mockito framework. I have injected the service class. I am using embedded h2 database.
When i tried to debug the written test case, I could see that it doesn't invoke the implementation class of the service method and returns empty array. I have attached debug screenshot of the controller class.
Below is the written JUnit class file:-
package com.testSpringBoot;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.testSpringBoot.controller.EmployeeController;
import com.testSpringBoot.dto.EmployeeDTO;
import com.testSpringBoot.repository.EmployeeRepository;
import com.testSpringBoot.service.EmployeeService;
import junit.framework.Assert;
#RunWith(SpringRunner.class)
#SpringBootTest
public class EmployeeControllerJUnit {
private MockMvc mockMvc;
#Mock
private EmployeeService employeeService;
#InjectMocks
private EmployeeController employeeController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(employeeController).build();
}
#Test
public void employeeControllerTest() throws Exception {
Mockito.when(employeeService.getAllEmployees()).thenReturn(new ArrayList<EmployeeDTO>());
mockMvc.perform(MockMvcRequestBuilders.get("/employee/getAllEmployees"))
.andExpect(status().isOk());
}
}
Debug Screenshot at controller file:-
Debug screenshot
Could you please let me know where i am making mistake.
Thanks
#Mock
private EmployeeService employeeService;
This instructs Mockito JUnit runner to create a mock of EmployeeService type. A mock does not use any implementation that you could write in your EmployeeService class (or in any of its implementations if EmployeeService is an interface). Instead, it just 'mocks' the behavior of that class.
Here, you instruct the mock to return an empty list when its getAllEmployees() method is called:
Mockito.when(employeeService.getAllEmployees()).thenReturn(new ArrayList<EmployeeDTO>());
And according to your description, it does just that.
You need to decide whether you need to mock your service or not. If not, don't use Mockito's mocks and just use your service implementation (although this will convert your test to an integration test).

Categories

Resources