I have catalog service that works with product service to get data (microservices). When I try to make getForObject in catalog service, I have an error 404.
#RestController
#RequestMapping("/catalog")
public class ProductCatalogApi {
#Autowired
private RestTemplate restTemplate;
#GetMapping("")
public String hello(){
return "Heelloooo";
}
#GetMapping("/{category}")
public void getProductsByCategoryName(#PathVariable String category) {
UserProduct userProduct = restTemplate.getForObject(
"http://shop-product-service/shop/products" + category,
UserProduct.class);
System.out.println("dsdasa--------"+ userProduct);
}
This is my product service:
#RestController
#RequestMapping("/shop")
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/all")
public List<Product> index(){
return productRepository.findAll();
}
#GetMapping("/product/{id_product}")
public Optional<Product> showByProductId(#PathVariable String id_product){
return productRepository.findById(id_product);
}
#GetMapping("/products/{category}")
public List<Product> showByCategoryName(#PathVariable String category){
return productRepository.findByCategory(category);
}
}
So when I try to make link this: http://localhost:8082/catalog/electronics, I get error, Please help me.
you lost the character "/" in the class ProductCatalogApi :
restTemplate.getForObject("http://shop-product-service/shop/products" + category,
UserProduct.class);
http://shop-product-service/shop/products =>
http://shop-product-service/shop/products/
Related
i'm creating a simple crud with spring boot. All function well but I have a little problem with the method findOne(Long id)
in postman when i put this url : http://localhost:8080/api/user/id/?id=13 , i get this exception :
"error": "Internal Server Error",
"exception": "org.springframework.dao.InvalidDataAccessApiUsageException",
"message": "The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!",
here is my code :
Repository
#SuppressWarnings("unused")
#Repository
public interface UserRepository extends JpaRepository<Utilisateur, Long> {
}
Service
public interface UserService {
Utilisateur save(Utilisateur utilisateur);
List<Utilisateur> findAll();
Utilisateur findOne(Long id);
}
ServiceImpl
#Service
public class UserServiceImpl implements UserService{
#Autowired
UserRepository userRepository;
public UserServiceImpl() {
}
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public Utilisateur save(Utilisateur utilisateur) {
return userRepository.save(utilisateur);
}
#Override
public List<Utilisateur> findAll() {
return userRepository.findAll();
}
public Utilisateur findOne(Long id) {
return userRepository.findOne(id);
}
}
Controller
#RestController
#RequestMapping("/api")
#CrossOrigin(origins="http://localhost:4200",allowedHeaders="*")
public class UserController {
#Autowired
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public UserController() {
super();
// TODO Auto-generated constructor stub
}
#GetMapping("/users")
public List<Utilisateur> getUsers() {
return userService.findAll();
}
#GetMapping("/user/id/{id}")
public ResponseEntity<Utilisateur> getUser(#PathVariable Long id) {
Utilisateur utilisateur = userService.findOne(id);
return ResponseEntity.ok().body(utilisateur);
}
/*
#DeleteMapping("/user/{id}")
public Boolean deleteUser(#PathVariable Long id) {
userRepository.delete(id);
return true;
} */
#PostMapping("/user")
public ResponseEntity<Utilisateur> saveUser(#RequestBody Utilisateur utilisateur) throws URISyntaxException {
Utilisateur result = userService.save(utilisateur);
return ResponseEntity.created(new URI("/api/user/" + result.getId()))
.body(result);
}
#PutMapping("/user")
public ResponseEntity<Utilisateur> updateUser(#RequestBody Utilisateur utilisateur) throws URISyntaxException {
Utilisateur result = userService.save(utilisateur);
return ResponseEntity.ok().body(result);
}
}
You have your mapping set as a URL path variable
#GetMapping("/user/id/{id}")
but the URL you tried has a query parameter: ?id=13
Try using: http://localhost:8080/api/user/id/13
Here is a good comparison of the two on stackoverflow
The URL is incorrect. You have set it up as a path variable in your code. In which case, instead of hitting localhost:8080/api/user/id/?id=13 in postman, you should hit localhost:8080/api/user/id/3 instead,
But if you were following REST standards, a better URL would look like this (no need to have the "id" in the URL). localhost:8080/api/user/3
I am new to Unit testing and TDD. I want to apply unit testing for my controller and service class which i have written in spring boot.
I have implemented test class using a tutorial. However, I could not implemented it successfully. I have included my current code.
Controller
#RestController
#RequestMapping("/api")
public class MyController {
private static final Logger LOGGER = LoggerFactory.getLogger(AdminController.class);
#Autowired
MyService myService;
#PostMapping("/create")
public ResponseEntity<?> createUser(#RequestHeader("Authorization") String token,
#RequestBody User user){
ResponseDTO finalResponse = new ResponseDTO();
try {
ResponseEntity<?> entity = myService.create(token, user);
finalResponse.setMessageCode(entity.getStatusCode());
finalResponse.setMessage("Success");
finalResponse.setError(false);
ResponseEntity<ResponseDTO> finalEntity = ResponseEntity.ok().body(finalResponse);
return finalEntity;
} catch (Exception e) {
finalResponse.setMessageCode(HttpStatus.EXPECTATION_FAILED);
finalResponse.setMessage(e.getMessage());
finalResponse.setError(true);
ResponseEntity<ResponseDTO> finalEntity =
ResponseEntity.ok().body(finalResponse);
return finalEntity;
}
}
ResponseDTO
public class ResponseDTO {
private HttpStatus messageCode;
private String message;
private String messageDetail;
private Object body;
private boolean error;
//setters and getters
}
Current Test Class
#RunWith(SpringRunner.class)
public class MyControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post("/api/create")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
}
When i run test class i am getting WARN Resolved [org.springframework.web.bind.MissingRequestHeaderException: Missing request header 'Authorization' for method parameter of type String]
What am i doing wrong here? Any help would be grateful.
Your test could be something like this:
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post("/api/create")
.accept(MediaType.APPLICATION_JSON)
.header("AUTH_TOKEN", TOKEN)
.content(ObjectToJsonUtil.convertObjectToJsonBytes(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
you'll have to convert object user to json.
So you create a util class for this:
public class ObjectToJsonUtil {
public static byte[] convertObjectToJsonBytes(Object object)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
JavaTimeModule module = new JavaTimeModule();
mapper.registerModule(module);
return mapper.writeValueAsBytes(object);
}
}
Hope it helps!
There are a few issues with your test:
1. Request mapping
#PostMapping("/create")
public ResponseEntity<?> createUser(
#RequestHeader("Authorization") String token,
#RequestBody User user)
only matches POST requests that have an HTTP header named Authorization and a request body that can be serialized to User. These are not optional. If they are optional, you should explicitly declare that:
#PostMapping("/create")
public ResponseEntity<?> createUser(
#RequestHeader(name = "Authorization", required = false) String token,
#RequestBody(required = false) User user) {
Assuming that they are required, you should setup MockMvc to send both to your controller:
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(
post("/api/create")
.header("Authorization", "XYZ")
.content("{\"firstName\": \"James\", \"lastName\": \"Gosling\"}")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
here I have assumed your User class is like this:
public class User {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
2. Content-Type header
Also, you should set the content type header for your MockMvc request, otherwise the test will fail with 415 - Unsupported Media Type. So your test should look like this:
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(
post("/api/create")
.header("Authorization", "XYZ")
.header("Content-Type", "application/json")
.content("{\"firstName\": \"James\", \"lastName\": \"Gosling\"}")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
3. Mocked dependencies
Apart from that, in your test you have annotated MyController with #InjectMocks but you haven't mocked its MyService' dependency. That will set themyServicefield of your controller tonull. To fix that you need to mockMyService' too:
#RunWith(SpringRunner.class)
public class MyControllerTest {
private MockMvc mockMvc;
// Mock
#Mock
private MyService myService;
#InjectMocks
private MyController myController;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
#Test
public void testCreateUser() throws Exception {
// Configure mock myService
when(myService.create(anyString(), any(User.class))).thenReturn(new ResponseEntity<>(HttpStatus.CREATED));
mockMvc.perform(
post("/api/create")
.header("Authorization", "XYZ")
.header("Content-Type", "application/json")
.content("{\"firstName\": \"James\", \"lastName\": \"Gosling\"}")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.*", Matchers.hasSize(1)));
}
}
4. MyService does not satisfy the test conditions
When everything is hunky dory your controller responds with:
ResponseEntity<ResponseDTO> finalEntity = ResponseEntity.ok().body(finalResponse);
which will return a status code of 200. So you either have to modify your test to expect that:
.andExpect(status().isOk())
or you should update your controller to return with the 201 status code:
ResponseEntity<ResponseDTO> finalEntity = ResponseEntity.created(null).body(finalResponse);
"Fixed":
My endpoint was returning void instead of the User so it couldn't get the values.
I'm making tests using webTestClient for my application and the POST test has no value at JSON.
Code:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
public class UserResourceTest {
#Autowired
private WebTestClient webTestClient;
#Test
public void testCreateUser() {
User user = new User(1L, "Slavik", 19);
webTestClient.post()
.uri("/api/user/")
.contentType(APPLICATION_JSON_UTF8)
.accept(APPLICATION_JSON_UTF8)
.body(Mono.just(user), User.class)
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.id").isNotEmpty()
.jsonPath("$.username").isEqualTo("Slavik");
}
#Test
public void getAllOkForGetAll() {
webTestClient.get()
.uri("/api/user/")
.accept(APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(APPLICATION_JSON_UTF8)
.expectBodyList(User.class);
}
}
The GET method works.
Removing the both JSON lines makes the test pass.
.expectBody(User.class) does not work.
Expecting status OK is correct for my case (should be Create but it's not the issue)
Removing $. also did not work
The Endpoint:
#PostMapping
#ResponseStatus(value = HttpStatus.OK)
public void add() {
this.userService.add();
}
User class:
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
private Long id;
private String username;
private Integer age;
}
I'm building a microservice architecture with Spring Framework, Feign and Eureka.
Shop works as an API Gateway, realised with Zuul (no database)
Each microservice has its own h2 database.
The CustomerService can call cart mappings with Feign
Example: I have a "Shop" and want to create a customer with a cart in one method in the service itself.
How can I call methods in the ShopService, to manage the other Services without having relationships to the other services? Please let me know if you need code examples.
CustomerController.java:
#ComponentScan
#RestController
public class CustomerController {
final Customer2CartConnectorRequester customer2CartConnectorRequester;
final Customer2OrderConnectorRequester customer2OrderConnectorRequester;
#Autowired
private CustomerJpaRepository customerJpaRepository;
#Autowired
public CustomerController(Customer2CartConnectorRequester customer2CartConnectorRequester,
Customer2OrderConnectorRequester customer2OrderConnectorRequester) {
this.customer2CartConnectorRequester = customer2CartConnectorRequester;
this.customer2OrderConnectorRequester = customer2OrderConnectorRequester;
}
#GetMapping("/list")
#ResponseBody
public List<CustomerEntity> getCustomers() {
return customerJpaRepository.findAll();
}
#RequestMapping("/{customerId}")
#ResponseBody
public Optional<CustomerEntity> getCustomer(#PathVariable("customerId") int customerId) {
return customerJpaRepository.findById(customerId);
}
// Client to Server
// POST über z.B. Postman Client
#PostMapping("/customer")
public CustomerEntity addCustomer(#RequestBody CustomerEntity customerEntity) {
customerJpaRepository.save(customerEntity);
return customerEntity;
}
#DeleteMapping("customer/{customerId}")
public String deleteCustomer(#PathVariable int customerId) {
CustomerEntity a = customerJpaRepository.getOne(customerId);
customerJpaRepository.delete(a);
return "deleted";
}
#PutMapping("/customer")
public CustomerEntity updateCustomer(#RequestBody CustomerEntity customerEntity) {
customerJpaRepository.save(customerEntity);
return customerEntity;
}
/// CONNECTOR REQUESTER ///
#GetMapping("/cart")
public List<?> getCart() {
return customer2CartConnectorRequester.getCart();
}
#GetMapping("/orders")
public List<?> getOrders() {
return customer2OrderConnectorRequester.getOrders();
}
}
Update:
I tried using a POST-Method to send data via Postman Client like this, but it seems like there is still something wrong:
Shop2CustomerConnectorRequester:
#FeignClient("customermicroservice")
public interface Shop2CustomerConnectorRequester {
#RequestMapping(value = "/list", method = RequestMethod.POST)
public ResponseEntity<String> createCustomer(Map<String, ?> queryMap);
}
ShopController:
#ComponentScan
#RestController
public class ShopController {
final Shop2CustomerConnectorRequester shop2CustomerConnectorRequester;
#Autowired
private ShopJpaRepository shopJpaRepository;
#Autowired
public ShopController(Shop2CustomerConnectorRequester shop2CustomerConnectorRequester) {
this.shop2CustomerConnectorRequester = shop2CustomerConnectorRequester;
}
#GetMapping("/customer")
public ResponseEntity<String> createCustomer(Map<String, ?> queryMap) {
return shop2CustomerConnectorRequester.createCustomer(queryMap);
}
}
I have simple Spring boot Rest application what returns Users list from database.
Application works as expected but test scenario fail with error. After long googling cannot figure out why?
It seems that test class cannot access userRepository and instead of calling userRepository.getAllUsers it calls AppController.getAllUsers.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
……………………………
Caused by: java.lang.NullPointerException
at com.loan.demo.controller.AppController.getAllUsers(AppController.java:43)
…………………………………………..
These are my classes:
LoanAppApplication
#SpringBootApplication
public class LoanAppApplication {
public static void main(String[] args) {
SpringApplication.run(LoanAppApplication.class, args);
}
}
Class User.java
#Entity
#Table(name="USERTABLE")
public class User {
private int id;
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
private String persID;
private int blocked;
private Set<Loan> loans;
public User() {
}
public User(String firstName, String lastName, String persID) {
this.firstName = firstName;
this.lastName = lastName;
this.persID = persID;
}
UserRepository:
#Repository
public interface UserRepository extends JpaRepository<User, Integer>{
public User findById(int Id);
public User findByPersID(String userId);
}
And Rest Controller:
#RestController
public class AppController {
#Autowired
UserRepository userRepository;
#GetMapping("/doit")
public String doIt() {
return "Do It";
}
//list all users
#GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll(); // this is line 43 from debuging error log
}
}
And test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {LoanAppApplication.class})
public class LoanAppApplicationTests {
private MockMvc mockMvc;
#InjectMocks
private AppController appController;
#Before
public void addData() {
mockMvc = MockMvcBuilders.standaloneSetup(appController)
.build();
}
//First test scenario that return only string works perfectly
#Test
public void testData() throws Exception {
mockMvc.perform(get("/doit")
)
.andExpect(status().isOk())
.andExpect(content().string("Do It"));
}
//but second that should return empty json string fails with exception
#Test
public void testGet() throws Exception {
mockMvc.perform(get("/users")
)
.andExpect(status().isOk())
.andExpect(content().string("Do It")); //this test should fail but not return exception
}
}
You need to mock your userRepository
#Mock
UserRepository userRepository;
so after #Mock you need to initialize Mock`s in #Before, add this:
MockitoAnnotations.initMocks(this);
then in code setup what users you want to get
User user = new User();
when(userRepository.getUsers()).thenReturn(Collections.singletonList(user));
and then check
verify(userRepository, times(1)).getUsers();
verifyNoMoreInteractions(userRepository);
this is because you application context is not working