spring controller test by mock service - java

Love Spring Testing Even More With Mocking and Unit Test Assistant:
A mocked service replaces multiple dependencies
enter image description here
#Controller
#RequestMapping("/people")
public class PeopleController {
#Autowired
protected PersonService personService;
#GetMapping
public ModelAndView people(Model model) {
for (Person person: personService.getAllPeople()) {
model.addAttribute(person.getName(), person.getAge());
}
return new ModelAndView("people.jsp", model.asMap());
}
}
private MockMvc mockMvc:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
private MockMvc mockMvc;
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
I get a mistake when I want to run mockMvc
java.lang.NullPointerException

Perform the following steps:
create service mock instead of service original
("PersonServiceMock")
replace service original by service mock
#Autowired
PersonService personService;
#Autowired
PeopleController peopleController;
private MockMvc mockMvc;
#Before
public void setup() {
peopleController = new PeopleController(new personServiceMock());
mvc = MockMvcBuilders.standaloneSetup(peopleController).build();
}
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}

That's because you are never initialising mockMvc in your code and the point where you access it results in nullPointerException. You need to initialise it before using it, and since multiple tests in your class could be using it, best place to do it is setup() method annotated with #before. Try below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}

from the source code I see that the mockMvc doesn't have any value, thats why it hits "java.lang.NullPointerException" for this line of code :
ResultActions actions = mockMvc.perform(get("/people"));
to make it run, I think need to give value to mockMvc first.
by constructor :
#Test
public void testPeople() throws Exception {
mockMvc = new MockMvc();
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
or Autowired :
#Autowired
MockMvc mockMvc
depends on the purpose of MockMvc Class

Related

how can i insert advanced data in spring boot test?

I'm making test code in spring boot.
But, my test code doesn't save the data using #Before method.
If i request to '/v1/stay/, it return empty array...
Please can you explain what is wrong with my code?
Here is my test code.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class StayControllerTest {
#MockBean
private StayService stayService;
#Autowired
private MockMvc mockMvc;
// givenStay method is the method generating dummy data
#Before
public void before() {
stayService.save(givenStay1());
stayService.save(givenStay2());
stayService.save(givenStay3());
stayService.save(givenStay4());
stayService.save(givenStay5());
}
#Test
#Transactional
void showStayList() throws Exception {
List<StayReq> original = new ArrayList<>();
original.add(givenStay1());
original.add(givenStay2());
original.add(givenStay3());
original.add(givenStay4());
original.add(givenStay5());
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/v1/stay")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(result.getResponse());
}
}
And below code blocks are my StayController and StayService
#RestController
#ApiV1
#RequiredArgsConstructor
public class StayController {
private final StayService stayService;
private final ApiService apiService;
#GetMapping("/stay")
public ResponseEntity<Response> stayList() {
return apiService.okResponse(stayService.getList());
}
}
#Service
#RequiredArgsConstructor
public class StayService {
private final StayRepository stayRepository;
private final RoomRepository roomRepository;
public List<StayRes> getList() {
return stayRepository.findAll().stream().map(StayRes::new).collect(Collectors.toList());
}
#Transactional
public void save(StayReq stayReq) {
stayRepository.save(stayReq.toEntity());
}
}
You injected a mock, not a 'real' service. If you want to use a 'real' service - you need to replace #MockBean annotation with #Autowired annotation.
Or alternatively - you can configure mock in the test method to return some predefined data.

Can't pass mock to the inject mock service

I have service:
#Service
class UserService {
private final Map<AbstractSomeService, CustomEnum> someMap;
public UserService(List<AbstractSomeService> someService) {
someService.forEach(service -> someMap.put(service.getCustomEnum(), service));
}
public void logicExecution() {
//code
}
}
When i am mocking as below: i am getting NullPointer:
#Mock
private SomeService someService; // Service which is extended of AbstractSomeService
#InjectMocks
private UserService userService = new UserService(Collection.singletonList(someService))
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
public void testBrokenJunit() {
userService.logicExecution(); // NULL POINTER HERE (
}
SomeService:
#Service
public class SomeService extends AbstactSomeService() {
public CustomEnum getCustomEnum() {
return CustomEnum.BROKEN_JUNIT_TEST;
}
//logic here
}
Stack trace is quite simple:
java.lang.NullPointerException: Cannot invoke "getCustomEnum()" because "service" is null
StackTrace without constructor initialization:
org.mockito.exceptions.misusing.InjectMocksException:
Cannot instantiate #InjectMocks field named 'UserService' of type '...'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Cannot invoke "java.util.List.forEach(java.util.function.Consumer)" because "someService" is null
at
Caused by: java.lang.NullPointerException: Cannot invoke "java.util.List.forEach(java.util.function.Consumer)" because "someService" is null
at UserService.<init>
P.S.
When i am using real object of UserService, not a mock everything is ok.
But it doesn't work with #InjectMocks
Try the following in your test class (you don't need to initialize UserService with a new instance via the constructor, #InjectMocks will do that for you):
#Mock
private SomeService someService;
#InjectMocks
private UserService userService;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
public void testBrokenJunit() {
userService.logicExecution();
}
If this does not work because you have a List and not a simple SomeService you can do the following:
#Mock
private SomeService someService;
private UserService userService;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
userService = new UserService(Collection.singletonList(someService));
}
#Test
public void testBrokenJunit() {
userService.logicExecution();
}
Use #MockBean to create mock objects
import org.springframework.boot.test.mock.mockito.MockBean;
#MockBean
private User user;

Inject with MockMVC Test

I'm writing an Spring MVC integration test and want to mock an external service, which is embedded within the class structure. However, I can't seem to get the mock to work.
This is my class structure:
Controller:
#RestController
public class Controller {
private final MyService service;
#Autowired
public Controller(MyService service) {
this.service = service;
}
#RequestMapping(value = "/send", method = POST, produces = APPLICATION_JSON_VALUE)
public void send(#RequestBody Response response) {
service.sendNotification(response);
}
}
Service:
#Service
public class MyService {
// Client is external service to be mocked
private final Client client;
private final Factory factory;
#Autowired
public MyService(Client client, Factory factory) {
this.factory = factory;
this.client = client;
}
public void sendNotification(Response response) {
// Implemented code some of which will be mocked
}
}
Integration Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class IntegrationTest {
MockMvc mockMvc;
#Autowired
MyService service;
#Mock
Client client;
#Autowired
Factory factory;
#InjectMocks
Controller controller;
#Before
public void setup() {
initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void test1() {
String json = "{/"Somejson/":/"test/"}";
mockMvc.perform(post("/send")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isCreated());
}
}
This results in the service ending up as null. Can anyone spot what I am doing wrong? Thanks
Good thing is you are using constructor Injection in Controller and Service class. Which makes it easier to initialize with mocks
This should work.
public class IntegrationTest {
MockMvc mockMvc;
MyService service;
Controller controller;
#Mock
Client client;
#Autowired
Factory factory;
#Before
public void setup() {
initMocks(this);
MyService service = new MyService(client, factory);
controller = new Controller(service);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}

How to create a bean of repository class

I'm doing unit test using spring mvc test framework.
The following is my source code:
com.exmple.main
MyController.java
#Controller
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public Map<Object, Object> myControllerFunction(#RequestBody final Object jsonRequest) {
/* do something */
return response;
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<My, String> {
#Query(value="select * from my d where (d.start_date<to_date(:date,'YYYY/DD/MM')) and (d.end_date>to_date(:date,'YYYY/DD/MM'))", nativeQuery=true)
List<My> findByDate(#Param("date") String date);
}
MyService.java
public interface MyService {
List<My> findByDate(String date);
}
MyServiceImpl.java
#Service
public class MyServiceImpl implements MyService {
#Autowired
MyRepository destRepo;
#Override
public List<My> findByDate(String date) {
List<My> listDest = destRepo.findByDate(date);
return listDest;
}
}
com.example.test
MyControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
MyService myService;
#Autowired
protected WebApplicationContext webApplicationContext;
#Before
public void setup() throws Exception {
// this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void listAllMy() throws Exception {
}
}
TestConfig.java
#Configuration
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new MyServiceImpl();
}
}
When I run test, the following error is displayed
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
I know the exception occurred because MyService didn't find any bean of MyRepository.
But I don't know how to create a bean of repository.
Please teach me how to create a bean of repository class using Java (not xml).
You need to enable the JPA repositories in your config class, specify the package that contains the repositories as below
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.repository"
})
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new DestinationServiceImpl();
}
}
Edit: looks like you haven't defined entityManager, and dataSource. Refer to a tutorial here and also answer to similar question here

Spring MVC Test failing

I am trying to run a Spring MVC Test but keep getting this exception.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
The exception is occuring because the autowired dependency,
#Autowired
private AccountService accountService;
is not getting injected in the test (works fine outside of the test).
Can anyone help me with this. Here is my code:
//AccountControllerITest Class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
public class AccountControllerITest {
private MockMvc mvc;
ObjectMapper om;
#Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
#Test
public void getAccounts() throws Exception {
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get("/api/accounts"))
.andExpect(status().isOk())
.andReturn();
}
}
}
//AccountController
#RestController
#RequestMapping("/api/accounts")
public class AccountController {
#Autowired
private AccountService accountService;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Set<AccountInfo>> getAccounts(#RequestParam(value = "firstName", required = false) String firstName,
#RequestParam(value = "surName", required = false) String surName) {
Set<AccountInfo> accounts = accountService.getAccounts(firstName, surName);
return new ResponseEntity<>(accounts, HttpStatus.OK);
}
}
Thanks for the help!
Because you are using standalone setup: mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();. If you create controller via new AccountController(), Spring doesn't have a chance to inject accountService as it doesn't control instances creation and wiring.
You have two options:
Switch your test to unit test and do not use SpringJUnit4ClassRunner nor MockServletContext at
all. You can use #InjectMocks to inject private accountService:
public class AccountControllerITest {
private MockMvc mvc;
ObjectMapper om;
#Mock
private AccountController accountController = new AccountController();
#InjectMocks
private AccountService accountService = new Mockito.mock(AccountService.class);
#Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.standaloneSetup(accountController).build();
}
There is also enhancement you can apply to your controller. Replace field injection with constructor injection and you can pass accountService to controller via constructor in test. This way you don't need to use #InjectMocks at all.
Use webAppContext setup:
#Autowired
private WebApplicationContext webApplicationContext;
#BeforeMethod
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
You need to configure your test to inject your autowired properties. See the following code block:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class AccountControllerITest {
// ... your test code
#Configuration
public static class YourConfiguration {
#Bean
AccountService accountService() {
// init and return your AccountService
}
}
}
You can init your AccountService with Mockito.

Categories

Resources