I'm trying to find some manual how to test POST methods using jersey framework, only got examples for GET method.
Here's example:
#POST
#Path("add")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response addUser(JAXBElement<User> user) {
int code = userService.addUser(user.getValue());
if (code == 500) {
return Response.status(500).build();
}
return Response.status(code).entity(user).build();
}
Could you please post some POST method test example?
Thank you in advance.
After research I did it!
Here's my solution, it works just fine.
And it's rather integration test, but we can write unit tests in similar manner.
public class RestTest extends JerseyTest{
#Override
protected Application configure() {
return new Your_Resource_Config(); //Your resource config with registered classes
}
//#Before and/or #After for db preparing etc. - if you want integration tests
#Test
public void addUserTest() {
User user = new User();
user.setEmail("user2#mail.com");
user.setName("Jane Doe");
user.getUserRoles().getRoles().add("supertester");
Entity<User> userEntity = Entity.entity(user, MediaType.APPLICATION_XML_TYPE);
target("users/add").request().post(userEntity); //Here we send POST request
Response response = target("users/find").queryParam("email", "user2#mail.com").request().get(); //Here we send GET request for retrieving results
Assert.assertEquals("user2#mail.com", response.readEntity(User.class).getEmail());
}
Related
I do want to write some functional tests for our project. Techstack: Play Framework 1.5, Java 16, Junit 3.
I found following documentation:
test - 1.5.x
security - 1.5.x
So the Controller looks something like this.
#AllowFeature(Feature.SEARCH_BOX)
public static void search(String term) {
//implementation omitted
TablePage<MyAwesomeType> page = search(term);
render(..., page, term);
}
And my test looks like this
public class SearchTest extends FunctionalTest {
#Test
public void search_withResults() {
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
}
However, the TablePage<MyAwesomeType> page is null when it really should not be, and i am unable to step into the controller method with the debugger. So it looks like the controller method search(...) is not called at all.
The response Code is 302 - Found but I think this might be play suggestion it found the path /foo/search
My guess is that i need to setup some UserContext or send a authenticityToken along with the request. So play can check the required feature #AllowFeature(Feature.A_SEARCH_BOX).
Does anybody know how I would setup such a functional test?
Any help is appreciated. Thanks!
I was able to figure this out.
I need to log into the application and then the play FunctionalTest.class takes care of the cookie.
Add #NoAuthenticityto the login method
#NoAuthenticity // <-- This enables execution without authenticityToken
public static void login(#Required String username, #Required String password) {
...
}
Post a request to login before the test.
#Test
public void search_withResults() {
// 1. login
Map<String, String> credentials = Map.of("username", "MyUsername", "password", "MyPassword");
POST("/login", credentials);
// Note: session info / authenticityToken is stored in a cookie
// FunctionalTest.class makes sure to use this cookie for subsequent requests
// This request now works like a charm
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
Note: One can use the JUnit #Before Annotation to simplify the test class.
#Before
public void login(){
Map<String, String> credentials = Map.of("username", "MyUsername", "password", "MyPassword");
POST("/login", credentials);
}
#Test
public void search_withResults() {
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
#Test
public void anotherTest() { ... }
#Test
public void yetAnotherTest() { ... }
Hello I'm trying to create a POST method and I keep getting the "404 Request method 'GET' not supported" error. Below I'll post my Rest controller and below that I'll post my service class. The only thing not working is the #PostMapping method.
#RequestMapping("/ATM")
public class ATMController {
private ATMService atmService;
#Autowired
public ATMController(ATMService atmService) {
this.atmService = atmService;
}
#GetMapping(path = "/{id}")
public ATM getATMById(#PathVariable long id){
return atmService.getByID(id);
}
#PostMapping(path = "/{id}/withdraw/{amount}")
public List<Bill> withdrawMoney(#PathVariable long id,#PathVariable float amount){
return atmService.withdrawMoney(id,amount);
}
}
#Service
public class ATMService {
private ATMRepository atmRepository;
private BillRepository billRepository;
#Autowired
public ATMService(ATMRepository atmRepository, BillRepository billRepository) {
this.atmRepository = atmRepository;
this.billRepository = billRepository;
}
public void save(ATM atm) {
atmRepository.save(atm);
}
public ATM getByID(Long id) {
return atmRepository.findById(id).get();
}
public List<Bill> getBillList(Long id) {
return atmRepository.findById(id).get().getBillList();
}
#Transactional
public List<Bill> withdrawMoney(Long id, float amount) {
List<Bill> allBills = getBillList(id);
List<Bill> billsToWithdraw = new ArrayList<>();
float amountTransferred = 0;
for (Bill bill : allBills) {
if (bill.getValue() == 100) {
billsToWithdraw.add(bill);
amountTransferred += bill.getValue();
}
if (amountTransferred == amount) {
for (Bill billToWithdraw : billsToWithdraw) {
billRepository.delete(billToWithdraw);
}
return billsToWithdraw;
}
}
return null;
}
}
I don't see the issue, I've tried switching to #GetMapping and removed the actual transaction "billRepository.delete(billToWithdraw);" and the method then returns the correct bills.
As the error says 404 Request method 'GET' not supported means you are making a GET request instead of POST.
You can make use of tools like Postman to make a post request. Hitting /{id}/withdraw/{amount} via any browser will prompt a GET request and not a POST request.
The issue is that you are sending a GET request to an end point that is configured to accept only POST request. This will probably help you to test them.
How to test
In case you GET requests -
You CAN directly check the api from the browser address bar. Type in the api and hit enter.Its that Simple!
You can use a tool such as Postman, SoapUI, etc to send a GET request.
You could write an html form with action="get mapping uri" and method="GET"
If your API uses any documentation or design tools such as swagger you can test it from its interface.
In case you POST requests -
You CANNOT directly check the api from the browser address bar.
You can use a tool such as Postman, SoapUI to send a POST request.
You could write an html form with action="post mapping uri" and method="POST".
If your API uses any documentation or design tools such as swagger you can test it from its interface.
In my case the problem was that I called https://localhost:8080/my-service but the port 8080 not supports HTTPS so I changed my call to http://localhost:8080 and resolved my problem. However when calling a http with https spring makes internally a GET Request
I am learning to implement web service using JAX-RS and JAXB, however I couldn't get this to work. The idea is to have a web service that can create a customer(with customer name) and store them in a Hash Map. I tried to create test as well but the test failed with error
javax.ws.rs.ProcessingException: Unable to invoke request
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke
(ApacheHttpClient4Engine.java:287)at
org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke
(ClientInvocation.java:407)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.post
(ClientInvocationBuilder.java:195)
at BloggerTest.addCustomer(StoreTest.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The Failure Trace says `Caused by :javax.ws.rs ProcessingException:
could not find writer for content-type application/xml type: store.domain.Customer
which I don't quite understand.
So below is what I have got at the moment, I'm using the "return singletons" to create instance of the store class:
A Store class:
public class Store {
private Map<String, Customer> _customer;
public Store() {
_customer = new ConcurrentHashMap <String,Customer>();
}
//assume the incoming http request contains customer name
#Post
#Consumes("application/xml")
public Response createCustomer (Customer customer){
_customer.put(customer.getName(), customer);
return Response.created( URI.create("/customers/" + customer.getName()))
.build();
}
}
And a class that runs test:
public class StoreTest {
private static final String WEB_SERVICE_URI = "http://localhost:10000/services/store";
private static Client _client;
#BeforeClass
public static void setUpClient() {
_client = ClientBuilder.newClient();
}
#Before
public void reloadServerData() {
Response response = _client
.target(WEB_SERVICE_URI).request()
.put(null);
response.close();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
#AfterClass
public static void destroyClient() {
_client.close();
}
#Test
public void addCustomer() {
Customer BartSimpsons = new Customer ("BartSimpsons");//create a new customer with name
Response response = _client
.target(WEB_SERVICE_URI).request()
.post(Entity.xml(BartSimpsons));
String location = response.getLocation().toString();
response.close();
Customer BartCreated = _client.target(location).request()
.accept("application/xml").get(Customer.class);
//check if the Customer created by the service has the same name with the original customer
assertEquals(BartSimpsons.getName(), BartCreated.getName());
}
}
I feel that I am missing some important points here, but I really couldn't figure how what I did wrong with the annotations or something else. Can someone please help? Many thanks in advance!
I also faced the same problem. Using #Produces annotation over my service method helped me to resolve the problem. Hope this will help you.
I need to test controllers that are secured with:
#Security.Authenticated(Secured.class).
Even after i log in i get unauthorized,i read that i need to copy the cookie from the log in response and send it with every request to secured method. tried that without any luck.
any idea how to solve that?
Assuming that you are using Helper.route method for testing, logged in behavior can be accomplished by using FakeRequest.withSession method.
For instance, if you are using email as authentication token in your Secured class;
#Override
public String getUsername(Http.Context ctx) {
return ctx.session().get("email");
}
Your test method would be like this;
#Test
public void testPage() {
FakeRequest testRequest = new FakeRequest(Helpers.GET, "/page")
.withSession("email", "mail#example.com");
Result result = Helpers.route(testRequest);
assertThat(Helpers.status(result)).isEqualTo(Helpers.OK);
}
To test our API that connects to the facebook graph API we use a mock server setup based on Jersey Test Framework and grizzly:
#Path("/" + PostRest.RESOURCE)
#Produces("application/json")
public class PostRest {
public static final String RESOURCE = "111_222";
#GET
public Response getPost(#QueryParam("access_token") String access_token) {
if (access_token != VALID_TOKEN) {
return Response.status(400).entity(createErrorJson()).build();
}
return Response.status(200).entity(createSomeJsonString()).build();
}
Now while I can react to an invalid or missing access_token with the correct error response, I also want to test that my API reacts correctly when trying to access an unkown resource at facebook ie an unkown path.
Right now I get a 404 from my grizzly obviously, if I try to access say "/111_2", but facebook seems to catch that error and wrap it inside a Json response, containing the string "false" with status 200.
So... How do I set up the Test Framework to return
Response.status(200).entity("false").build();
every time it is called for an known path?
Basic example:
#ContextConfiguration({ "classpath:context-test.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest extends JerseyTest {
#Inject
private SomeConnection connection;
private String unkownId = "something";
public SomeTest() throws Exception {
super("jsonp", "", "com.packagename.something");
}
#Test(expected = NotFoundException.class)
public void testUnkownObjectResponse() throws NotFoundException {
// here it should NOT result in a 404 but a JSON wrapped error response
// which will be handled by the Connection class and
// result in a custom exception
connection.getObject(unkownId);
}
Or maybe I can set up grizzly to behave as desired..?!
Thanks!
Obviously facebook has it own service to intercept errors. Same thing should be done in your code. Just expose you own test service that intercepts all request
#Path("/test/errorTrap")
public class ErrorTrapService{
....
}
This service will produce any response you want. So any un-existing pages like http://mytest/test/errorTrap/111_2 will be intercepted by test service and produce expected response for you