I'm attempting to write functional tests for my REST API using the Jersey Test framework. However, I've seem to hit a roadblock when it comes to using dependency injection within my functional tests. My main application looks like this:
#ApplicationPath("/")
public class Application extends ResourceConfig {
private static final URI BASE_URI = URI.create("http://localhost:8080/api/");
public static void main(String[] args) throws Exception {
System.out.println("Starting application...");
final ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(JacksonFeature.class);
resourceConfig.register(LoggingFeature.class);
resourceConfig.packages(true, "my.package.name");
final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, locator);
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
server.start();
Thread.currentThread().join();
}
}
Notice here that I'm using the HK2's ServiceLocatorUtilities.createAndPopulateServiceLocator() method in order to read the hk2-metadata-generator file. This method creates a ServiceLocator object which then in turn is passed to the GrizzlyHttpServerFactory.createHttpServer method. This all works great for running the Grizzly server, however, the question I have now is how do I create functional tests for my application with the Jersey Test Framework?
My unit test currently looks like this:
public class FormsResourceTest extends JerseyTest {
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
#Test
public void testMe() {
Response response = target("/test").request().get();
assertEquals("Should return status 200", 200, response.getStatus());
}
}
Is there even a way to use the HK2 service locator with the Jersey Test framework or do I need to treat my application as an external container and use the external container provider as documented here: External container?
Also, since these are functional tests, mocking the injected services is not an option here.
You can use the Locator Bridge to take two separate locator (the one you created and the one from Jersey) and bridge them together. The bridge can be made bi-directional as well (within limits) and so it'll appear in most normal usage to be one large ServiceLocator.
Note that there was a bug fixed this week with the ServiceLocator bridge which has not yet been pushed out to maven but will (probably) be pushed sometime next week. See HK2-295
Related
I'm running a test on Hello World trying to follow the Jersey Framework in my spring java program.
I have extended JerseyTest, but I'm getting the error above. The example Jersey gives link doesn't seem to help.
public class SimpleTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(RestService.class);
}
#Test
public void test() {
final String hello = target("\service\hello").request().get(HelloModel.class);
assertEquals("Hello World!", hello);
}
}
With Jersey 2.26 the dependency has changed to jersey-spring4, but to flush out the answer a little bit more if you don't want to spin up the entire Spring context.
Create the object:
final ResourceConfig resourceConfig = new ResourceConfig(restResource);
In my case, I just needed an empty context:
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
resourceConfig.property("contextConfig", context);
That was able to allow the JerseyTest to run successfully.
This is going to happen if you have the jersey-spring3 dependency. The default behavior is to look for this applicationContext.xml file (which is your Spring context configuration).
If you want to configure Spring for the test, you can do a couple things.
You could manually create the ApplicationContext and pass it to Jersey
ApplicationContext context = ...
return new ResourceConfig(RestService.class)
.property("contextConfig", context)
If you are using xml configuration, then you would create a ClassPathXmlApplicationContext. If you're using Java config, then you would create an AnnotationConfigApplicationContext.
If you need servlet servlet container support, check out the example in this post
I would like to test a class that provides a rest endpoint via JAX-RS. This class depends on a JPA EntityManager an thus on a database which needs to be populated prior to test execution. I saw solutions for database population like dbunit, but I want to populate the data directly from my test class (or delegated via object mother pattern). But when testing rest endpoints I need to use the annotation option #Deployment(testable = false) which refuses me to inject the EntityManager into my test class.
So how can I solve this situation?
Or are there any better best practices? (maybe mocking, but that's also not possible for black box tests)
You could create a bean to generate your test data:
#Startup
#Singleton
public class TestDataGenerator {
#PersistenceContext
private EntityManager em;
#PostConstruct
private void generateTestData() {
// Generate your test data
}
}
The TestDataGenerator class defined above is annotated with #Singleton (ensuring there will be only one instance of the class) and #Startup (for eager initialization during the application startup sequence).
Add the TestDataGenerator class to your Arquillian deployment:
#RunWith(Arquillian.class)
public class MyArquillianTest {
private Client client = ClientBuilder.newClient();
#Deployment
#RunAsClient
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class)
.addClasses(TestDataGenerator.class, ...)
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Test
public void testAPI(#ArquillianResource URL deploymentUrl) {
// Test your REST service
WebTarget target = client.target(deploymentUrl.toURI()).path("api");
}
}
Observe that #RunAsClient is equivalent to #Deployment(testable = false).
The #ArquillianResource annotation allows you to inject the URL to test your web application.
For tests, I usually try and separate black box and unit testing completely (I suppose it's a preference on how you do it).
For example, my REST Api could rely on whatever it wants, but usually it doesn't do much but call my database layer or some sort of facade accessing my DB layer. The objects are injected, yes, but usually I make the fields package private, which meant that you can set them from the same package (which works with Junit as well).
For example:
public class Facade1 {
#Inject
Facade facade;
public void doSomething() { ... }
}
This class represents my REST API. I could test doSomething by simply adding a mock object as facade. Mind you, this is a quite useless test, but you get the idea. Unit tests should happen in isolation with as much mocking as possible.
Now testing the actual Rest API I usually resort to a python integration tester. Python has nice http libraries that allow you to make request very easily.
For that, I set up a staging environment for my Rest Server. The environment is a live-like representation for testing. Everything there needs to works and needs to be on the same version as the production deployment.
I then use python to poke my REST Api and verify the responses. Since I've set up my staging environment I have complete control over database content. Therefore it is easy for me to test that all responses are correct.
My typical process then is:
Compile
Build
Deploy
Integration test
I hope that helps. If you want clearer examples, you might want to post a bit more code as it's a bit hard to imagine for me what it is exactly you'd like to do :)
In a Java Spring Boot application, I define the controllers path in a dedicated properties file. e.g.
#PropertySource("classpath:/my.properties")
#RequestMapping("${controller1.path}")
public class Controller{
#RequestMapping("/dosomething")
public void doSomethingREST(){
}
}
where my.properties looks like:
controller1.path=rest/path
The path of the REST service will be then http://localhost:8080/rest/path/dosomething
How can I read the path in the unit test class?
Should I necessarily write it manually?
Suppose that I change it, doesn't seem to be a very flexible approach.
Suggestions to make it more dynamic?
As far as I know, there is no way to unit-test the value of an annotation, unless you would use reflection - but in that case you would in fact have written a "change detector", which is probably not what you want.
You could write an integration test however. The "Testing" chapter from the Spring Boot Reference Guide provides an introduction how you could integration test your application.
Basically, the procedure is as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(classes = SampleDataJpaApplication.class)
public class MyTest {
#Value("${local.server.port}")
int port;
RestTemplate template = new TestRestTemplate();
#Test
public void testRequest() throws Exception {
template.getForEntity("http://localhost:" + port + "/rest/path/dosomething", String.class);
// Somehow verify that your application did the right thing, e.g. because some mocked component was called or the system is in a certain state.
}
}
Say I have the following route:
from(rabbitMQUri)
.to(myCustomerProcessor)
.choice()
.when(shouldGotoA)
.to(fizz)
.when(shouldGotoB)
.to(buzz)
.otherwise()
.to(foo);
Let's pretend that myCustomProcessor tunes shouldGotoA and shouldGotoB according to the message consumed from RabbitMQ.
I would like to unit test 3 scenarios:
A "fizz" message is consumed and shouldGotoA is set to true, which executes the first when(...).
A "buzz" message is consumed and shouldGotoB is set to true, which executes the second when(...).
A "foo" message is consumed and the otherwise() is executed.
My question is: how do I mock/stub the RabbitMQ endpoint so that the route executes as it normally will in production, but so that I don't have to actually connect the test to a RabbitMQ server? I need some kind of "mock message" producer.
A code example or snippet would be extremely helpful and very much so appreciated!
This is one way of putting together a suitable test.
Firstly define an empty Camel Context with just a ProducerTemplate in it:
<camel:camelContext id="camelContext">
<camel:template id="producerTemplate" />
</camel:camelContext>
I do this so that when I execute the test, I can control which routes actually start as I don't want all my routes starting during a test.
Now in the test class itself, you'll need references to the producer template and Camel Context. In my case, I'm using Spring and I autowire them in:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/spring/spring-test-camel.xml" })
public class MyTest {
#Autowired
private ProducerTemplate producerTemplate;
#Autowired
private CamelContext camelContext;
In the test itself, replace the RabbitMQ/ActiveMQ/JMS component in the context with the seda or direct component. eg replace all JMS calls with a seda queue.
camelContext.removeComponent("jms");
camelContext.addComponent("jms", this.camelContext.getComponent("seda"));
camelContext.addRoutes(this.documentBatchRouting);
Now whenever you are reading or writing to a JMS URI, it is actually going to a seda queue. This is similar to injecting a new URI into the component but take less configuration and allows you to add new endpoints to the route without worrying about remembering to inject all the URIs.
Finally in the test, use the the producer template to send a test message:
producerTemplate.sendBody("jms:MyQueue", 2);
You're route should then execute and you can test your expectations.
Two things to note are:
Your transaction boundaries may change, especially if you replace JMS queues with a direct component
If you are testing several routes, you'll have to be careful to remove the route from the Camel Context at the end of the tests for that route.
It may depend what component you are using (AMQP or RabbitMQ) for the communication.
The single most important resource for sample code in Camel is the junit test cases in the source.
Two files that does similar things to what you need are these two, but you may want to look around in the test cases in general to get inspiration:
AMQPRouteTest.java
RabbitMQConsumerIntTest.java
A more "basic" way to make routes testable is to make the "from" uri a parameter.
Let's say you make your RouteBuilder something like this:
private String fromURI = "amqp:/..";
public void setFromURI(String fromURI){
this.fromURI = fromURI;
}
public void configure(){
from(fromURI).whatever();
}
Then you can inject a "seda:foobar" endpoint in the fromURI before your start the unit test. The seda endpoint is trivial to test. This assumes you don't need to test AMQP/RabbitMQ specific constructs, but simply receive the payload.
A good way to make software better testable (especially software that communicates to external stuff) is to use dependency injection. I love Guice and it is directly supported by camel.
(all this stuff will burden you with learning about dependency injection but it will pay very soon i can assure you)
In this scenario you would just inject "Endpoint"s. You pre-configure the endpoints like this (would be placed in "module").
#Provides
#Named("FileEndpoint")
private Endpoint fromFileEndpoint() {
FileEndpoint fileEndpoint = getContext().getEndpoint("file:" + somFolder, FileEndpoint.class);
fileEndpoint.setMove(".done");
fileEndpoint.setRecursive(true);
fileEndpoint.setDoneFileName(FtpRoutes.DONE_FILE_NAME);
...
return fileEndpoint;
}
Your RouteBuilder just inject the endpoint:
#Inject
private MyRoutes(#Named("FileEndpoint") final Endpoint fileEndpoint) {
this.fileEndpoint = fileEndpoint;
}
#Override
public void configure() throws Exception {
from(fileEndpoint)....
}
To easily test such an route you inject another endpoint for test not FileEndpoint but "direct:something". A very easy way to do this is "Jukito", it combines Guice with Mockito. A test would look like this:
#RunWith(JukitoRunner.class)
public class OcsFtpTest extends CamelTestSupport {
public static class TestModule extends JukitoModule {
#Override
protected void configureTest() {
bind(CamelContext.class).to(DefaultCamelContext.class).in(TestSingleton.class);
}
#Provides
#Named("FileEndpoint")
private Endpoint testEndpoint() {
DirectEndpoint fileEndpoint = getContext().getEndpoint("direct:a", DirectEndpoint.class);
return fileEndpoint;
}
}
#Inject
private MyRoutes testObject;
#Test
....
}
Now the "testObject" will get the direct endpoint instead of the file endpoint.This works with all kinds of Endpoints and generally with all Interfaces/ abstract classes and Apis that heavily rely on Interfaces (camel excels here!).
I have a small app that interacts with a remote android service. I would like to mock that service in unit tests. I use Robolectric and JUnit for other test cases and shadows but I could not figure how to deal with remote services.
Is it sufficient to create and start a test service using the same package with the real service and export methods using same aidl?
Since I don't have the code for that service, I assume that I can not use Robolectric's ShadowService which requires actual class to be there.
Thanks a lot.
I would use Mockito to create a Mock of the interface and then pass that instance to your code in your tests. You could also manually create an implementation of that interface in your test code and use that.
So you have to do the mocking yourself and it is important that the code you want to tests uses some form of dependency injection to aquire a reference to the aidl interface, so you can pass your own mock in your tests.
If you want to write a unit test for service then you can use Mockito for mocking service behavior.If you want to test your service on the real device then this is how you can connect with your service.
#RunWith(AndroidJUnit4.class)
public classRemoteProductServiceTest {
#Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule();
#Test
public void testWithStartedService() throws TimeoutException {
mServiceRule.startService(
new Intent(InstrumentationRegistry.getTargetContext(), ProductService.class));
//do something
}
#Test
public void testWithBoundService() throws TimeoutException, RemoteException {
IBinder binder = mServiceRule.bindService(
new Intent(InstrumentationRegistry.getTargetContext(), ProductService.class));
IRemoteProductService iRemoteProductService = IRemoteProductService.Stub.asInterface(binder);
assertNotNull(iRemoteProductService);
iRemoteProductService.addProduct("tanvi", 12, 12.2f);
assertEquals(iRemoteProductService.getProduct("tanvi").getQuantity(), 12);
}
}