No value at JSON path during webTestClient entity creation test - java

"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;
}

Related

Posting data in a Spring Boot 2 Application with Spring Data JPA

I am trying to post data from postman via my Spring Boot 2 Application with Spring Data JPA into a MySQL Database. All I get is a 404 Error.
Main
#SpringBootApplication
public class ProfileApplication {
public static void main(String[] args) {
SpringApplication.run(ProfileApplication.class, args);
}
}
Entity
#Entity
public #Data class Profile {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String profileText;
}
Controller
#RestController
#RequestMapping(value = "/profile", produces = { MediaType.APPLICATION_JSON_VALUE })
public class ProfileController {
#Autowired
private ProfileRepository profileRepository;
public ProfileRepository getRepository() {
return profileRepository;
}
#GetMapping("/profile/{id}")
Profile getProfileById(#PathVariable Long id) {
return profileRepository.findById(id).get();
}
#PostMapping("/profile")
Profile createOrSaveProfile(#RequestBody Profile newProfile) {
return profileRepository.save(newProfile);
}
}
Repository
public interface ProfileRepository extends CrudRepository<Profile, Long> {
}
application.propterties
server.port = 8080
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/profiledb
spring.datasource.username=root
spring.datasource.password=
server.servlet.context-path=/service
It seems that in your ProfileController, you have defined twice the profile endpoint (first at the class level, and second on the methods). The solution would be to remove one of them:
#RestController
#RequestMapping(value = "/profile", produces = { MediaType.APPLICATION_JSON_VALUE })
public class ProfileController {
#Autowired
private ProfileRepository profileRepository;
public ProfileRepository getRepository() {
return profileRepository;
}
// Notice that I've removed the 'profile' from here. It's enough to have it at class level
#GetMapping("/{id}")
Profile getProfileById(#PathVariable Long id) {
return profileRepository.findById(id).get();
}
// Notice that I've removed the 'profile' from here. It's enough to have it at class level
#PostMapping
Profile createOrSaveProfile(#RequestBody Profile newProfile) {
return profileRepository.save(newProfile);
}
}
Which url? Valid url must look like:
GET: http://localhost:8080/service/profile/profile/1
POST: http://localhost:8080/service/profile/profile

Getting 404 error when try to resttemplate getforobject

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/

How to write unit test for controller in spring boot

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);

How to update the data in Spring Boot MVC

I have the most common project on Spring Boot MVC. and I'm trying to write update data via PUT.
#RestController
#RequestMapping(CommentController.PATH)
public class CommentController {
public final static String PATH = "/comments";
#Autowired
private CommentService service;
#PutMapping("/{id}")
public Comment update(#RequestBody Comment comment, #PathVariable Long id) {
return service.update(id, comment);
}
}
#Service
public class CommentService {
#Autowired
private CommentRepository repository;
public Comment update(Long id, Comment entity) {
Optional<Comment> optionalEntityFromDB = repository.findById(id);
return optionalEntityFromDB
.map(e -> saveAndReturnSavedEntity(entity, e))
.orElseThrow(getNotFoundExceptionSupplier("Cannot update - not exist entity by id: " + id, OBJECT_NOT_FOUND));
}
private Comment saveAndReturnSavedEntity(Comment entity, Comment entityFromDB) {
entity.setId(entityFromDB.getId());
return repository.save(entity);
}
}
#Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
}
#Entity
public class Comment {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#Column(name = "name")
protected String name;
}
then I write a test with the ability to check for updated data:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
// DBUnit config:
#DatabaseSetup("/comment.xml")
#TestExecutionListeners({
TransactionalTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
public class CommentControllerTest {
private MockMvc mockMvc;
private static String route = PATH + "/{id}";
#Autowired
private CommentController commentController;
#Autowired
private CommentRepository commentRepository;
#PersistenceContext
private EntityManager entityManager;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(commentController)
.build();
}
#Test
public void update_ShouldReturnCreated2() throws Exception {
int id = 1;
String name = "JohnNew";
Comment expectedComment = new Comment();
expectedComment.setName(name);
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(expectedComment);
this.mockMvc.perform(put(route, id)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json))
.andDo(print());
entityManager.clear();
entityManager.flush();
Comment commentUpdated = commentRepository.findById(1L).get();
assertThat(commentUpdated.getName(), equalTo(name)); // not equals!
}
}
comment.xml:
<dataset>
<Comment id="1" name="John" />
</dataset>
but the problem is that the data is not updated.
If you enable the logging of Hibernat, then there is also no update request to the database.
What am I doing wrong?
You are missing off the #Transactional annotation from your CommentService. Whilst it can be better to add it at the per-method level, try adding it to class level to verify this fixes things:
#Service
#Transactional
public class CommentService {

#PostMapping to other microservices with Feign and Eureka

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);
}
}

Categories