I'm getting very confused over whether you can or can't run spring boot stuff and REST endpoints in one application. At the moment I have them in separate project directories, running the springboot UI one with:
#SpringBootApplication
public class LeagueProjectUiApplication {
public static void main(String[] args) {
SpringApplication.run(LeagueProjectUiApplication.class, args);
}
}
and the REST rest endpoints with:
mvn tomcat7:run
and my jersey and tomcat stuff are declared in my pom.xml
Rest:
#Path("/university")
public class University {
#GET
#Path("/{universitycode}")
#Produces(MediaType.APPLICATION_JSON)
public Response returnSingleSummoner(
#PathParam("universitycode") String universityCode) {
}
What's the best way of running both SpringBoot and REST endpoints at the same time, or am I getting completely confused!
Thanks.
When you say REST endpoints, do you mean Jersey endpoints?
Spring Boot supports Jersey,as you can see, for example, here, so nothing theoretically should stop you for putting everything in one application as long as request paths are different.
I am trying to test Cassandra with JUnit External resources. I have two classes CassandraExternalResource and TestCassandra. Here is snippet from TestCassandra:
public class TestCassandra {
#Rule
public CassandraExternalResource cassandraExternalResource = new CassandraExternalResource();
#Test
public void test() throws InterruptedException {
System.out.println("During Test");
}
}
In before method of CassandraExternalResource I start Cassandra Server with a separate thread. What I want is to make test cases wait until cassandra server is up and running.
Since server takes some time to start, test cases start to run before server is ready.
Any help is appreciated.
You can extend the rule's before method by pinging the cassandra server until it is ready.
I have the below route. In unit test, since I doesn't have the FTP server available, I'd like to use camel's test support and send an invalid message to "ftp://hostname/input" and verify that it failed and routed to "ftp://hostname/error".
I gone through the documentation which mainly talks about using the "mock:" endpoint but I am not sure how to use it in this scenario.
public class MyRoute extends RouteBuilder
{
#Override
public void configure()
{
onException(EdiOrderParsingException.class).handled(true).to("ftp://hostname/error");
from("ftp://hostname/input")
.bean(new OrderEdiTocXml())
.convertBodyTo(String.class)
.convertBodyTo(Document.class)
.choice()
.when(xpath("/cXML/Response/Status/#text='OK'"))
.to("ftp://hostname/valid").otherwise()
.to("ftp://hostname/invalid");
}
}
As Ben says you can either setup a FTP server and use the real components. The FTP server can be embedded, or you can setup a FTP server in-house. The latter is more like an integration testing, where you may have a dedicated test environment.
Camel is very flexible in its test kit, and if you want to build an unit test that do not use the real FTP component, then you can replace that before the test. For example in your example you can replace the input endpoint of a route to a direct endpoint to make it easier to send a message to the route. Then you can use an interceptor to intercept the sending to the ftp endpoints, and detour the message.
The advice with part of the test kit offers these capabilities: http://camel.apache.org/advicewith.html. And is also discussed in chapter 6 of the Camel in action book, such as section 6.3, that talks about simulating errors.
In your example you could do something a like
public void testSendError() throws Exception {
// first advice the route to replace the input, and catch sending to FTP servers
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:input");
// intercept valid messages
interceptSendToEndpoint("ftp://hostname/valid")
.skipSendToOriginalEndpoint()
.to("mock:valid");
// intercept invalid messages
interceptSendToEndpoint("ftp://hostname/invalid")
.skipSendToOriginalEndpoint()
.to("mock:invalid");
}
});
// we must manually start when we are done with all the advice with
context.start();
// setup expectations on the mocks
getMockEndpoint("mock:invalid").expectedMessageCount(1);
getMockEndpoint("mock:valid").expectedMessageCount(0);
// send the invalid message to the route
template.sendBody("direct:input", "Some invalid content here");
// assert that the test was okay
assertMockEndpointsSatisfied();
}
From Camel 2.10 onwards we will make the intercept and mock a bit easier when using advice with. As well we are introducing a stub component. http://camel.apache.org/stub
Have a look at MockFtPServer!
<dependency>
<groupId>org.mockftpserver</groupId>
<artifactId>MockFtpServer</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
With this one you can simulate all sorts of behaviors like permission problems, etc:
Example:
fakeFtpServer = new FakeFtpServer();
fakeFtpServer.setServerControlPort(FTPPORT);
FileSystem fileSystem = new UnixFakeFileSystem();
fileSystem.add(new DirectoryEntry(FTPDIRECTORY));
fakeFtpServer.setFileSystem(fileSystem);
fakeFtpServer.addUserAccount(new UserAccount(USERNAME, PASSWORD, FTPDIRECTORY));
...
assertTrue("Expected file to be transferred", fakeFtpServer.getFileSystem().exists(FTPDIRECTORY + "/" + FILENAME));
take a look at this unit test and those in the same directory...they'll show you how to standup a local FTP server for testing and how to use CamelTestSupport to validate scenarios against it, etc...
example unit test...
https://svn.apache.org/repos/asf/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FromFileToFtpTest.java
which extends this test support class...
https://svn.apache.org/repos/asf/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsServerTestSupport.java
In our project we do not create mock FTP Server to test the route but we use properties that can be replaced by a file Camel Component for the local development and unit testing.
Your code would look like this:
public class MyRoute extends RouteBuilder
{
#Override
public void configure()
{
onException(EdiOrderParsingException.class)
.handled(true)
.to("{{myroute.error}}");
from("{{myroute.input.endpoint}}")
.bean(new OrderEdiTocXml())
.convertBodyTo(String.class)
.convertBodyTo(Document.class)
.choice()
.when(xpath("/cXML/Response/Status/#text='OK'"))
.to("{{myroute.valid.endpoint}}}")
.otherwise()
.to("{{myroute.invalid.endpoint}}");
}
}
And locally and for system test we use a file endpoint declared in the property file:
myroute.input.endpoint=file:/home/user/myproject/input
myroute.valid.endpoint=file:/home/user/myproject/valid
myroute.invalid.endpoint=file:/home/user/myproject/invalid
myroute.error=file:/home/user/myproject/error
or in a JUnit CamelTestSupport you can use the useOverridePropertiesWithPropertiesComponent method to set the properties you want to overrides.
As an alternative you can also use a "direct" route instead but you can miss some File options that can be tested by the unit test.
And we only test the FTP connection with the real system by setting the properties like this:
myroute.input.endpoint=ftp://hostname/input
myroute.valid.endpoint=ftp://hostname/valid
myroute.invalid.endpoint=ftp://hostname/invalid
myroute.error=ftp://hostname/error
With this you can also have different configuration for e.g production server that will differentiate from the Integration Test Environment.
Example of Properties for Production environment:
myroute.input.endpoint=ftp://hostname-prod/input
myroute.valid.endpoint=ftp://hostname-prod/valid
myroute.invalid.endpoint=ftp://hostname-prod/invalid
myroute.error=ftp://hostname-prod/error
In my opinion it is totally acceptable to use file endpoint to simplify the JUnit code and it will test the route only and not the connection.
Testing the connection is more like an Integration Test and should be executed on the real server connected with the real external system (in your case FTP servers, but can be other endpoints/systems as well).
By using properties you can also configure different URL's per environment (For example: we have 3 testing environments and one production environment, all with different endpoints).
For (JUnit) testing purposes I'd like to make a simple application that would be the server to be invoked using Spring HttpInvoker. I don't want to make a real webapp to be deployed in any servlet container, only something standalone.
Do you have any ideas how to make it as simply as possible? (Solutions without embedded Tomcat or stuff are preferred..)
This will work out well for you - http://docs.codehaus.org/display/JETTY/ServletTester
#BeforeClass
public static void initServletContainer() throws Exception {
tester = new ServletTester();
tester.setContextPath("/");
tester.addServlet(DummyServlet.class, "/dummy");
baseUrl = tester.createSocketConnector(true);
tester.start();
System.out.println(baseUrl);
}
You can start up the server in your #BeforeClass method, record the baseUrl where the server starts up and use this url to test your client.
http://code.google.com/p/jianwikis/wiki/SpringHttpRemotingWithEmbeddedJettyServer
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.