I'm currently looking for ways to create automated tests for a JAX-RS (Java API for RESTful Web Services) based web service.
I basically need a way to send it certain inputs and verify that I get the expected responses. I'd prefer to do this via JUnit, but I'm not sure how that can be achieved.
What approach do you use to test your web-services?
Update: As entzik pointed out, decoupling the web service from the business logic allows me to unit test the business logic. However, I also want to test for the correct HTTP status codes etc.
Jersey comes with a great RESTful client API that makes writing unit tests really easy. See the unit tests in the examples that ship with Jersey. We use this approach to test the REST support in Apache Camel, if you are interested the test cases are here
You can try out REST Assured which makes it very simple to test REST services and validating the response in Java (using JUnit or TestNG).
As James said; There is built-in test framework for Jersey. A simple hello world example can be like this:
pom.xml for maven integration. When you run mvn test. Frameworks start a grizzly container. You can use jetty or tomcat via changing dependencies.
...
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.16</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.16</version>
<scope>test</scope>
</dependency>
</dependencies>
...
ExampleApp.java
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/")
public class ExampleApp extends Application {
}
HelloWorld.java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/")
public final class HelloWorld {
#GET
#Path("/hello")
#Produces(MediaType.TEXT_PLAIN)
public String sayHelloWorld() {
return "Hello World!";
}
}
HelloWorldTest.java
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.core.Application;
import static org.junit.Assert.assertEquals;
public class HelloWorldTest extends JerseyTest {
#Test
public void testSayHello() {
final String hello = target("hello").request().get(String.class);
assertEquals("Hello World!", hello);
}
#Override
protected Application configure() {
return new ResourceConfig(HelloWorld.class);
}
}
You can check this sample application.
You probably wrote some java code that implements your business logic and then you have generated the web services end point for it.
An important thing to do is to independently test your business logic. Since it's pure java code you can do that with regular JUnit tests.
Now, since the web services part is just an end point, what you want to make sure is that the generated plumbing (stubs, etc) are in sync with your java code. you can do that by writing JUnit tests that invoke the generated web service java clients. This will let you know when you change your java signatures without updating the web services stuff.
If your web services plumbing is automatically generated by your build system at every build, then it may not be necessary to test the end points (assuming it's all properly generated). Depends on your level of paranoia.
Though its too late from the date of posting the question, thought this might be useful for others who have a similar question.
Jersey comes with a test framework called the Jersey Test Framework which allows you to test your RESTful Web Service, including the response status codes. You can use it to run your tests on lightweight containers like Grizzly, HTTPServer and/or EmbeddedGlassFish. Also, the framework could be used to run your tests on a regular web container like GlassFish or Tomcat.
I use Apache's HTTPClient (http://hc.apache.org/) to call Restful Services. The HTTP Client library allows you to easily perform get, post or whatever other operation you need. If your service uses JAXB for xml binding, you can create a JAXBContext to serialize and deserialize inputs and outputs from the HTTP request.
Take a look at Alchemy rest client generator. This can generate a proxy implementation for your JAX-RS webservice class using jersey client behind the scene. Effectively you will call you webservice methods as simple java methods from your unit tests. Handles http authentication as well.
There is no code generation involved if you need to simply run tests so it is convenient.
Dislclaimer: I am the author of this library.
Keep it simple. Have a look at https://github.com/valid4j/http-matchers which can be imported from Maven Central.
<dependency>
<groupId>org.valid4j</groupId>
<artifactId>http-matchers</artifactId>
<version>1.0</version>
</dependency>
Usage example:
// Statically import the library entry point:
import static org.valid4j.matchers.http.HttpResponseMatchers.*;
// Invoke your web service using plain JAX-RS. E.g:
Client client = ClientBuilder.newClient();
Response response = client.target("http://example.org/hello").request("text/plain").get();
// Verify the response
assertThat(response, hasStatus(Status.OK));
assertThat(response, hasHeader("Content-Encoding", equalTo("gzip")));
assertThat(response, hasEntity(equalTo("content")));
// etc...
An important thing to do is to independently test your business logic
I certainly would not assume that the person who wrote the JAX-RS code and is looking to unit test the interface is somehow, for some bizarre, inexplicable reason, oblivious to the notion that he or she can unit testing other parts of the program, including business logic classes. It's hardly helpful to state the obvious and the point was repeatedly made that the responses need to be tested, too.
Both Jersey and RESTEasy have client applications and in the case of RESTEasy you can use the same annoations (even factor out annotated interface and use on the client and server side of your tests).
REST not what this service can do for you; REST what you can do for this service.
As I understand the main purpose of the auther of this issue is to decouple JAX RS layer from business one. And unit test only the first one. Two basic problems here we have to resolve:
Run in test some web/application server, put JAX RS components in
it. And only them.
Mock business services inside JAX RS
components/REST layer.
The first one is solved with Arquillian.
The second one is perfectly described in arquillican and mock
Here is an example of the code, it may differ if you use another application server, but I hope you'll get the basic idea and advantages.
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import com.brandmaker.skinning.service.SomeBean;
/**
* Created by alexandr on 31.07.15.
*/
#Path("/entities")
public class RestBean
{
#Inject
SomeBean bean;
#GET
public String getEntiry()
{
return bean.methodToBeMoked();
}
}
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import com.google.common.collect.Sets;
/**
*/
#ApplicationPath("res")
public class JAXRSConfiguration extends Application
{
#Override
public Set<Class<?>> getClasses()
{
return Sets.newHashSet(RestBean.class);
}
}
public class SomeBean
{
public String methodToBeMoked()
{
return "Original";
}
}
import javax.enterprise.inject.Specializes;
import com.brandmaker.skinning.service.SomeBean;
/**
*/
#Specializes
public class SomeBeanMock extends SomeBean
{
#Override
public String methodToBeMoked()
{
return "Mocked";
}
}
#RunWith(Arquillian.class)
public class RestBeanTest
{
#Deployment
public static WebArchive createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
.addClasses(JAXRSConfiguration.class, RestBean.class, SomeBean.class, SomeBeanMock.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
System.out.println(war.toString(true));
return war;
}
#Test
public void should_create_greeting() {
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://127.0.0.1:8181/test/res/entities");
//Building the request i.e a GET request to the RESTful Webservice defined
//by the URI in the WebTarget instance.
Invocation invocation = target.request().buildGet();
//Invoking the request to the RESTful API and capturing the Response.
Response response = invocation.invoke();
//As we know that this RESTful Webserivce returns the XML data which can be unmarshalled
//into the instance of Books by using JAXB.
Assert.assertEquals("Mocked", response.readEntity(String.class));
}
}
A couple of notes:
JAX RS configuration without web.xml is used here.
JAX RS Client is used here (no RESTEasy/Jersey, they expose more convenient API)
When test starts, Arquillian's runner starts working. Here you can find how to configure tests for Arquillian with needed application server.
Depending on the chosen application server, an url in the
test will differ a little bit. Another port may be used. 8181 is
used by Glassfish Embedded in my example.
Hope, it'll help.
Related
I am trying to start a mock a server from Java and keep it running to receive incoming requests from other sources (Postman, CURL, etc).
I have tried the Junit approach, but when the unit tests finishes, the server is shutdown.
On the other hand, running the standalone version
http://www.mock-server.com/mock_server/running_mock_server.html#running_from_command_line
keeps the mock server running.
I would like to achieve the same thing, but from the Java code.
The question is, how may I make it run and stay running?
Thanks
So you need an HTTP server for non-testing purposes? I'd try with Spring, something like:
#RestController
public class CatchAllController {
#RequestMapping("/**")
public String catchAll(HttpServletRequest request) {
return request.getRequestURI();
}
}
There is an example on that same page (paragraph "Client API - starting and stopping"). This code works for me:
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import org.mockserver.integration.ClientAndServer;
public class Checker {
public static void main(String[] args) {
ClientAndServer mockServer = startClientAndServer(1080);
}
}
You have to call
mockServer.stop();
later to stop it.
You will need the following maven dependency:
<!-- mockserver -->
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.5.1</version>
</dependency>
Collegues, I have a test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("dev")
public class ServiceIFTest {
#Autowired
ServiceIF ServiceIF; /*this is web service endpoint bean */
#Test
public void createMonIntBalTranDoc() {
TranDocReq parameter = new TranDocReq();
/*set variables*/
CreateTranDocRes tranDoc = serviceIF.createTranDoc(parameter);
assertNotEquals(java.util.Optional.ofNullable(tranDoc.getId()), 0);
}
}
it is a simple Junit test where I check class field value.
But I need to do an integration test, I would like to check an XML which service will return to the client.
At the current moment if found a quite old article https://www.ibm.com/developerworks/webservices/tutorials/ws-soa-autotest2/ws-soa-autotest2.html
where the author recommends writing client etc...
So the question is how to test integration between soap web service and client from Java project?
In other words, I need to check the XML response returned by my web service.
P.s. Some years ago I did it with SOAP UI and groovy. Also, I tried to use jaxbToString converter, but it cut namespaces.
I don't if this is even possible, which is why I thought I'd ask.
I forgot to mention I'm using Jersey 1.19 and Java 1.6.
I created a RESTful web service in Java using the Jersey API, as well as client code to call the web service. The client code is Jersey-based, as well. The problem I'm running into is I don't want to deploy the JAR file to the web server every time I make a change and want to test -- the web server is on a remote server and I"m coding on a local computer.
Is it possible to simulate a client-server web service call completely within the IDE (i.e. Eclipse)? In other words, I want to call the web service from my
local computer, without having to host it on a web server; no different than
calling a function.
Here is the client code:
package com.xyzcorp.webservices;
import javax.ws.rs.core.MediaType;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
public class RestClient {
public static void main(String[] args) {
Client client = Client.create();
/*
Right now, it's calling the web service on the web server.
I want to call this same web service but from within the code local
to my computer, without hosting it on a web server.
*/
WebResource webResource = client.resource("http://myserver.com/rest/ids/12345");
ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
}
}
Here is the web service code:
package com.xyzcorp.webservices;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.xyzcorp.webservices.EmpData;
#Path("/rest")
public class RestWebService {
#GET
#Path("/ids/{ids}")
#Produces(MediaType.APPLICATION_JSON)
public EmpData getEmpDataJSON(
#PathParam("ids") String ids)
...
return empData;
}
}
Is it possible to call the RestWebService class directly without having to use a web server? I.e. `WebResource webResource = client.resource(new RestWebService().EmpData("12345"));
Thank you very much.
Use Jersey Test Framework. Run (semi) Integration/Unit tests on your resources like you would a normal unit test. For example
public class MainTest extends JerseyTest {
public MainTest() throws Exception {
super("com.sun.jersey.samples.helloworld.resources");
}
#Test
public void testHelloWorld() {
WebResource webResource = resource();
String responseMsg = webResource.path("helloworld").get(String.class);
assertEquals("Hello World", responseMsg);
}
}
The JerseyTest will start and stop an embedded server for each test case. It could even be an in memory server (so as not to take so much load time) depending on what server dependency you want to use. Here's an example dependency
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly2</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
The link I provided shows other dependencies you can use.
Here is another example usage (the bottom part is for Jersey 1.x - the top is 2.x)
This might be of use to you:
http://www.eclipse.org/webtools/community/education/web/t320/Implementing_a_Simple_Web_Service.pdf
particularly page 12 onwards.
Hope it helps.
EDIT : just in case, your machine URL will contain "localhost" for services running on your local computer.
I have the task of creating a web service that takes requests from multiple clients. I am new to web services so i used this tutorial:
http://www.java-forums.org/blogs/web-service/1145-how-create-java-web-service.html
This is exactly what i am looking for. A web service with no java ee.
It is preferable that is stick with java se, it's a policy that is prefered to be kept.
Now i would like to go one step further and implement the service so that it processes requests from multiple clients that operate on a shared resource.
Ideally i would like something like this:
Client client = new Client();
client.processRequest(string);
And the web service will process the requests in the order they arrive. The requests will come in as an request is processed so it will be kept in a stack.
The problem is i just on't know how to send the response back to the specific client. The response will be a string. The only thing i came up with, at least in principle, is to send a object that remembers where it came from but that just seems the web services job.
I have searched the internet but did not find a solution.
If possible using only SE please help.
If you think it is not possible without EE you can say so, but i would very much like an answer using only SE
I think what you are trying to implement is an Asynchronous Webservice. The following link tells you how to implement it in Java SE.
http://java.dzone.com/articles/asynchronous-java-se-web
You can do this using the Endpoint.publish methods in Java SE. First, you create a simple "endpoint interface":
package com.example;
import javax.jws.WebService;
#WebService
public interface ExampleService {
String getDateTime();
}
Then you specify that interface in an implementation class. As you can see, both classes must be annotated with #WebService.
package com.example;
import java.io.IOException;
import java.net.URL;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Service;
import javax.xml.namespace.QName;
#WebService(endpointInterface = "com.example.ExampleService",
serviceName = "ExampleService",
portName = "timeService",
targetNamespace = "http://example.com/time")
public class ExampleServiceImpl
implements ExampleService {
public String getDateTime() {
return String.format("%tc", System.currentTimeMillis());
}
public static void main(String[] args)
throws IOException {
// Create server
Endpoint endpoint =
Endpoint.publish("http://localhost:8080/example",
new ExampleServiceImpl());
URL wsdl = new URL("http://localhost:8080/example?wsdl");
// Create client
Service service = Service.create(wsdl,
new QName("http://example.com/time", "ExampleService"));
ExampleService e = service.getPort(ExampleService.class);
// Test it out
System.out.println(e.getDateTime());
endpoint.stop();
}
}
By default, JAX-WS will treat all public methods of an endpoint interface as web methods (since that is commonly what developers will want). You can have more control by placing the #WebMethod annotation on only the methods you want exposed as web services.
See the JAX-WS specification for all the details.
I've been gradually piecing together how I can receive a serialized object within Spring and have gotten a web service working, by following a tutorial, that uses Jax-WS. I have verified that I can access this basic service through a browser by pulling up the XML page using a url like http://localhost:8080/WebServicesExample/hello?wsdl
The code I currently have is like below, however I want to make a service so that a serialized object can be passed in, for example a HashMap and then have spring de-serialize it, etc. I have been doing a lot of reading on this but am still a bit lost, I would appreciate if anyone can offer advice how to get from where I am at currently to what I am trying to do. Thanks
import javax.jws.WebMethod;
import javax.jws.WebService;
import com.mkyong.bo.HelloWorldBo;
#WebService
public class HelloWorldWS{
//DI via Spring
HelloWorldBo helloWorldBo;
#WebMethod(exclude=true)
public void setHelloWorldBo(HelloWorldBo helloWorldBo) {
this.helloWorldBo = helloWorldBo;
}
#WebMethod(operationName="getHelloWorld")
public String getHelloWorld() {
return helloWorldBo.getHelloWorld();
}
}
I am not completely sure which WS stack you have used for exposing this service, assuming that you just want to create a JAX-WS based sample, let me point you to a working sample that I had created earlier available at : git://github.com/bijukunjummen/memberservice-codefirst.git .
This sample uses Apache CXF as the JAX-WS implementation, and uses JAXB2 for binding (converting the incoming xml to Java objects and back)
In your example, Apache CXF would allow you to expose a WS interface using an entry into spring configuration files of the type:
<jaxws:endpoint address="/helloworldservice" id="helloworld" implementor="#helloworldBean" >
</jaxws:endpoint>