I have an ApplicationProperties that i use it in my service. When i mock the rest template in my test to test that service it results in java.lang.NullPointerException, when i replace applicationProperties.getBebusiness().getEndPointProvider() with the url, the test passes
#Configuration
#ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties { ...
}
#Service
public class ProviderServiceImpl implements ProviderService {
private final ApplicationProperties applicationProperties;
private final RestTemplate restTemplate;
public ProviderServiceImpl(ApplicationProperties applicationProperties, RestTemplate restTemplater) {
this.applicationProperties = applicationProperties;
this.restTemplate = restTemplate;
}
#Override
public List <ProviderDTO> getAllProviderFromBebusiness() {
final List <ProviderDTO> result = new ArrayList<>();
final ResponseEntity <String> responseEntity = restTemplate.getForEntity(
applicationProperties.getBebusiness().getEndPointProvider() + "&page=0&size=" + Integer.MAX_VALUE, String.class);
if (responseEntity.getStatusCodeValue() == 200) {}
return result;
}
}
public class ProviderServiceTest {
#Autowired
private ApplicationProperties applicationProperties;
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#InjectMocks
private ProviderServiceImpl providerService;
#Test
public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() {
String provider = providerInit.buildProviderDTOWithIdFromBebusiness();
ResponseEntity <String> responseEntity = new ResponseEntity <String> (provider, HttpStatus.OK);
when(restTemplate.getForEntity(anyString(), ArgumentMatchers.any(Class.class)))
.thenReturn(responseEntity);
List<ProviderDTO> result = providerService.getAllProviderFromBebusiness();
assertEquals(200, responseEntity.getStatusCodeValue());
}
}
Seems your spring beans are not instantiated. You have to annotate your test class to tell spring it has to initialize its context. Try use #SpringBootTest annotation
You can either use #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) annotation or if you don't want to run Spring Boot context then just mock your configuration class as following:
#BeforeEach // #Before if you're using JUnit4
void setUp() {
Bebusiness bebusiness = mock(Bebusiness.class);
when(applicationProperties.getBebusiness()).thenReturn(bebusiness);
when(bebusiness.getEndPointProvider()).thenReturn("http://localhost:8080");
}
You can use
#TestPropertySource(locations = "/other-location.properties")
Or
#TestPropertySource(properties = "spring.datasource.max-active=30,
spring.datasource.initial-size=5")
This is a exemple : https://www.logicbig.com/tutorials/spring-framework/spring-core/test-property-source-with-inlined-properties.html
Related
i'm learning yet testing in java and i'm trying to write an integration test for controller below:
public class OrderController {
#Autowired
private OrderService orderService;
public OrderController(OrderService service) {
this.orderService = service;
}
#PostMapping(value = "/add")
#ApiOperation(value = "Add", response = AddOrderResult.class)
public Response<AddOrderResult> add(#ModelAttribute AddUpdateOrderEnter enter) {
return new Response<>(orderService.addOrder(enter));
}
#PostMapping(value = "/addOrderParts")
#ApiOperation(value = "AddOrderParts", response = GeneralResult.class)
public Response<GeneralResult> AddOrderParts(#ModelAttribute #ApiParam("请求参数") AddOrderPartsEnter enter) {
return new Response<>(orderService.AddOrderParts(enter));
}
#GetMapping(value = "/list")
#ApiOperation(value = "Order List", response = OrderDetailsResult.class)
public Response<List<OrderDetailsResult>> list(#ModelAttribute #ApiParam("请求参数") GeneralEnter enter) {
return new Response<>(orderService.getOrderList(enter));
}
#GetMapping(value = "/details")
#ApiOperation(value = "Order Details", response = OrderDetailsResult.class)
public Response<OrderDetailsResult> details(#ModelAttribute #ApiParam("请求参数") IdEnter enter) {
return new Response<>(orderService.getOrderDetails(enter));
}
#PostMapping(value = "/delete")
#ApiOperation(value = "delete", response = GeneralResult.class)
public Response<GeneralResult> delete(#ModelAttribute IdEnter enter) {
return new Response<>(orderService.deleteOrder(enter));
}
}
My test file which i'm stuck:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
class OrderControllerTest {
#Autowired
private OrderService service;
private MockMvc mockMvc;
#Test
void shouldAddNewOrder() throws Exception {
}
So i want to have some example to how to test my controller in integration way. Thnks for helps :)
What i've done is below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(controllers = OrderControllerTest.class)
class OrderControllerTest {
#MockBean
private OrderService service;
#Autowired
private MockMvc mockMvc;
#Test
void shouldAddNewOrder() throws Exception {
AddUpdateOrderEnter enter = new OrderBuilder()
.orderId(1L)
.orderType(1)
.productId(2L)
.paymentTypeId("STRIPE")
.build();
mockMvc.perform(post("/order/add")
.contentType("application/json"))
.andExpect(status().isOk());
ArgumentCaptor<AddUpdateOrderEnter> orderCaptor = ArgumentCaptor.forClass(AddUpdateOrderEnter.class);
verify(service, times(1)).addOrder(orderCaptor.capture());
assertThat(orderCaptor.getValue().getOrderId()).isEqualTo(1L);
assertThat(orderCaptor.getValue().getOrderType()).isEqualTo(1);
}
Here is my Code
NullPointerException occurs in using restTemplate and postsRepository.
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private PostsRepository postsRepository;
#Test
public void register_posts() throws Exception{
// ...
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url,requestDto,Long.class);
List<Posts> all = postsRepository.findAll();
}
Here is my code. I can't see why it is not working. The problem is with the line in test :
when(applicationUserRepository.findApplicationUserByUsername("testUser"))
.thenReturn(userToReturnFromRepository);
doesn't seem to be doing anything. The function findApplicationUserByUsername returns an empty optional when it should be returning an optional of userToReturnFromRepository.
Controller:
#RestController
#RequestMapping("api/v1/exercises")
public class ExerciseController {
#Autowired
ExerciseService exerciseService;
#GetMapping
public List<Exercise> getExercises() {
List<Exercise> exercises = exerciseService.getAllExercises();
return exercises;
}
}
Service:
#Service("exerciseService")
public class ExerciseService {
#Autowired
ExerciseRepository exerciseRepository;
#Autowired
ApplicationUserRepository applicationUserRepository;
#Transactional
public List<Exercise> getAllExercises() {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
Optional<ApplicationUser> applicationUser = applicationUserRepository.findApplicationUserByUsername(principal.getName());
List<Exercise> exercises = new ArrayList<>();
if(applicationUser.isPresent()){
exercises = applicationUser.get().getExercises();
}
return exercises;
}
}
The test:
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {
private final MockMvc mockMvc;
private final ObjectMapper objectMapper;
#Mock
ApplicationUserRepository applicationUserRepository;
#Autowired
public ExerciseControllerTest(MockMvc mockMvc,
ApplicationUserRepository applicationUserRepository, ObjectMapper objectMapper) {
this.mockMvc = mockMvc;
this.applicationUserRepository = applicationUserRepository;
this.objectMapper = objectMapper;
}
#BeforeEach
public void initMocks() {
MockitoAnnotations.openMocks(this);
}
#Test
#WithMockUser(username = "testUser")
public void testGetExercises() throws Exception {
Exercise ex = new Exercise();
ex.setData("test");
ApplicationUser user = new ApplicationUser();
Exercise[] exercises = {ex};
List<Exercise> list = new ArrayList<Exercise>(Arrays.asList(exercises));
user.setExercises(list);
Optional<ApplicationUser> userToReturnFromRepository = Optional.of(user);
when(applicationUserRepository.findApplicationUserByUsername("testUser"))
.thenReturn(userToReturnFromRepository);
mockMvc.perform(get("/api/v1/exercises")).andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));
}
}
There are two conflicting things happening in your test:
You are using Mockito to init a mock implementation via reflection
You have ApplicationUserRepository being Spring injected into the Test class via the constructor.
What ends up happening is this:
spring injects applicationUserRepository into the constructor param
The applicationUserRepository field is set to the spring injected version in the constructor
Mockito inits a new applicationUserRepository mock
Mockito replaces the applicationUserRespository field with the mock (i.e. goodbye to your handle on the spring bean that your MVC setup is using!)
The easiest way I can think of to fix it is to use #MockBean instead of the #Mock combined with Constructor injection. #MockBean will instruct Spring to create the mock instance for you, use it, and provide it to you in the test.
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {
private final MockMvc mockMvc;
private final ObjectMapper objectMapper;
#MockBean // User MockBean instead of Mock
ApplicationUserRepository applicationUserRepository;
#Autowired
public ExerciseControllerTest(MockMvc mockMvc, ObjectMapper objectMapper) {
this.mockMvc = mockMvc;
//remove the applicationUserRepository injection
this.objectMapper = objectMapper;
}
// remove MockitoAnnotations.openMocks(this);
//...
...
}
I have a SpringBoot app. with this test, but it does not inject and mock the classes
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:servlet.xml"
})
public class TerritoriClandestiControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Mock
TerritoriClandestiRepository territoriClandestiRepository = mock(TerritoriClandestiRepository.class);
#InjectMocks
private TerritoriClandestiService territoriClandestiService;
List<Object[]> list;
Resource listResource = new ClassPathResource("list.txt");
#Before
public void setup() throws IOException {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.build();
list = DataLoader.readLines(listgetInputStream());
}
#Test
public void getAll() throws Exception {
when(territoriClandestiRepository.findAllBaseData(anyLong())).thenReturn(list);
mockMvc.perform(get("/terrcland")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(1)));
}
}
You can use #MockBean instead of #Mock, it will export the field as a bean in the spring context.
public class TerritoriClandestiControllerTest {
#MockBean
private TerritoriClandestiRepository territoriClandestiRepository;
}
Alternatively you can also do something like this
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:servlet.xml"
}, classes = {TerritoriClandestiControllerTest.Config.class})
public class TerritoriClandestiControllerTest {
#TestConfiguration
static class Config {
#Bean
TerritoriClandestiRepository territoriClandestiRepository() {
return Mockito.mock(TerritoriClandestiRepository.class);
}
}
#Autowired
private TerritoriClandestiRepository territoriClandestiRepository;
}
I have a test class using #WebMvcTest that looks like this
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#WebMvcTest({HomeController.class, ShoppingCartController.class})
#Import({SecurityConfig.class, SecurityUtility.class, UserDetailsServiceImpl.class, UserRepository.class}) //#TestPropertySource("classpath:application.properties")
public class ShoppingCartControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BookService bookService;
And now I'm getting a lot of unsatisfied bean dependency exceptions because #WebMvcTest uses slicing, My question is how can I import all the dependency without repeating #Import 20 times and how do I use #MockBean?
Previously I used
#AutoConfigureMockMvc
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = BookstoreApplications.class, properties = "classpath:application.properties")
public class ShoppingCartControllerTest {
#Autowire
private MockMvc mockMvc
#Autowired
private HomeController homeController;
#Autowired
private ShoppingCartController shoppingCartController;
And all tests were passed now I am getting a default password in the log , which means I haven't imported the Authentication Context so it used Default Authentication.
Below is the whole class as it is now
AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#WebMvcTest({HomeController.class, ShoppingCartController.class})
#Import({SecurityConfig.class,UserRepository.class}) //#TestPropertySource("classpath:application.properties")
public class ShoppingCartControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SecurityUtility securityUtility;
#MockBean
UserDetailsServiceImpl userDetailsService;
#MockBean
private BookService bookService;
#MockBean
private UserService userService;
#MockBean
private CartItemService cartItemService;
#Configuration
#Import({PropertyTestConfiguration.class})
static class ContextConfiguration {
}
#Test
public void showLoginPage() throws Exception {
mockMvc.perform(get("/login")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
)
.andExpect(model().attributeExists("classActiveLogin"))
.andReturn();
}
#Test
#WithMockUser(username = "V", authorities = {"USER"})
public void addItemToShoppingCart() throws Exception {
CartItem cartItem = new CartItem();
String qty = "2";
Book book = new Book();
User user = new User();
book.setId(1L);
book.getId();
cartItem.setBook(book);
when(userService.findByUsername(anyString())).thenReturn(user);
when(bookService.findOne(anyLong())).thenReturn(book);
when(cartItemService.addBookToCartItem(book, user, Integer.parseInt(qty))).thenReturn(cartItem);
ObjectMapper mapper = new ObjectMapper();
String bookAsString = mapper.writeValueAsString(book);
mockMvc
.perform(get("/shoppingCart/addItem")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
.param("book", bookAsString)
.param("qty", qty))
.andReturn();
}
#Test
public void checkBookDetail() throws Exception {
Book book = new Book();
book.setId(1L);
when(bookService.findOne(anyLong())).thenReturn(book);
mockMvc
.perform(get("/bookDetail")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
.param("id", "1"))
.andExpect(model().attributeExists("book"))
.andExpect(model().attributeExists("qty"))
.andExpect(model().attributeExists("qtyList"))
.andReturn();
}
#Test
public void showBookShelf() throws Exception {
List<Book> bookList = new ArrayList<>();
when(bookService.findAll()).thenReturn(bookList);
mockMvc.perform(get("/bookshelf")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
)
.andExpect(model().attributeExists("activeAll"))
.andReturn();
}
}
I also think its wrong to use #Import because I am testing two classes as I have specified in #WebMvcTest, every other bean should use #MockBean. And also I am not allowed to use #SpringBootTest And for some reason test addItemToShoppingCart passes while others fail