The new Spring Shell docs don't seem to provide any examples of how to integration test CLI commands in a Spring Boot context. Any pointers or examples would be appreciated.
The method Shell#evaluate() has been made public and has its very specific responsibility (evaluate just one command) for exactly that purpose. Please create an issue with the project if you feel like we should provide more (A documentation chapter about testing definitely needs to be written)
Here is how I got this working.
You first need to override the default shell application runner to avoid getting stuck in the jline loop. You can do this by defining your own such as:
#Component
public class CliAppRunner implements ApplicationRunner {
public CliAppRunner() {
}
#Override
public void run(ApplicationArguments args) throws Exception {
//do nothing
}
}
Note that you will have to associate this custom Application runner against a "Test" profile so it overrides only during integration testing.
If you want to test a shell command "add 1 3", you then can write a test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes =CliConfig.class)
public class ShellCommandIntegrationTest {
#Autowired
private Shell shell;
#Test
public void runTest(){
Object result=shell.evaluate(new Input(){
#Override
public String rawText() {
return "add 1 3";
}
});
DefaultResultHandler resulthandler=new DefaultResultHandler();
resulthandler.handleResult(result);
}
}
Note that the above test does not Assert anything. You will probably have to write your own little implementation of the ResultHandler interface that deals with parsing/formatting of the result so that it can be asserted.
Hope it helps.
Spring Shell 2.0.1 depends on Spring Boot 1.5.8, which in turn depends on Spring Framework 4.3.12. This makes researching how to implement tests challenging, since the latest version of Spring Shell does not depend on the latest versions of other Spring libraries. Take a look at my example project, sualeh/spring-shell-2-tests-example which has example unit, functional and integration tests for a sample Spring Shell application.
Related
I have a spring boot application for which I want to create separate profiles for external and embedded tomcat. How can I check in my method which profile is active so that I can run the code based on particular profile.
I came up with some code as shown below.
if("${spring.active.profile}".contains("external_tomcat_profile")) {
//do something;
}else{
//another thing;
}
The above code does not work. How can I implement this functionality? or Is there and better way of doing this?
And I am using two profiles one "test" for testing and another either embedded or external tomcat, so is it correct to use this condition
if("${spring.active.profile}".contains("external_tomcat_profile"))
You can use the Environment bean for that
#Autowired
Environment env;
public void aMethod() {
String[] activeProfiles = env.getActiveProfiles();
}
This question is somewhat similar to this existing question
I am still trying to navigate or trying to find right spring boot code, which i can customize. I need to develop java SDK which connects with existing config server and provides values to key. This SDK will be used in java applications, which might or might not be spring application. Same SDK will be used by QA for regression testing of config server.
So question is, if given
Config server URL
application name
active profile (no need for label, it will be default master),
Can I initialize some config client class which will give me simple methods like public String getKeyValue(final String key)
I am looking at source of classes like ConfigServicePropertySourceLocator, CompositePropertySource, ConfigClientAutoConfiguration, ConfigServiceBootstrapConfiguration etc.
Do I need to build Environment object manually? If yes, how?
I have some success. Posting a possible answer for others to further fine tune it.
#SpringBootApplication
public class ConfigSDKApp {
#Autowired
public SomeSpringBean someBean = null;
private static ConfigSDKApp INSTANCE = null;
public synchronized static ConfigSDKApp getInstance(String[] args) {
if (null != INSTANCE) {
return INSTANCE;
}
SpringApplication sprApp = new SpringApplication(ConfigSDKApp.class);
sprApp.setWebEnvironment(false);
ConfigurableApplicationContext appContext = sprApp.run(args);
ConfigSDKApp app = appContext.getBean(ConfigSDKApp.class);//new ConfigSDKApp();
INSTANCE = app;
return INSTANCE;
}
}
It's kind of singleton class (but public constructor). Hence code smell.
Also, what if this SDK is running with-in springboot client. ApplicationContext & environment is already initialized.
I have written Camel Route build in Java DSL and now I wanted to debug it in eclipse IDE, my class look like
public class PMRouteBuilder extends RouteBuilder {
UserProfileResponseProcessor responseProcessor=new UserProfileResponseProcessor();
System.out.println("\n");
System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
System.out.println(" STARTED PROCESS MANAGER ROUTEBUILDER ");
System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
try{
from("cxf:bean:process-manager-ws?dataFormat=POJO").routeId("process-manager-route-userprofile").log( "This is ${in.header.operationName} operation called...." )
.log( "Entering inside the Choice with operation....${in.header.operationName}")
//.wireTap(RouterEndPoints.ENDPOINT_AUDITOR_QUEUE.value(),true, new PreWireTapProcessor())
.choice()
/**
* ##################################### ROUTE FOR USER PROFILE REQUEST ###########################################
*/
.when(simple("${in.header.operationName} == 'retrieveUserProfile'"))
.to("log:?showAll=true&multiline=true")
.setHeader("OPERATION_NAME", constant("retrieveUserProfile") )
.process(pmRequestProcessor)
.log( "Setting header value to...."+constant(AuditActions.Actions.ACTION_GET_USER_PROFILE.desc()) )
.setHeader(RouteActions.Actions.OMGMEAT_ACTION_ID.desc(), constant(AuditActions.Actions.ACTION_GET_USER_PROFILE.desc())).convertBodyTo(UserProfile.class)
.to(RouterEndPoints.ENDPOINT_USERPROFILE_QUEUE.value()).process(responseProcessor)
.when(simple("${in.header.operationName} == 'addUserProfile'"))
.log( "Setting header value to...."+constant(AuditActions.Actions.ACTION_ADD_PROFILE.desc()) )
.setHeader(RouteActions.Actions.OMGMEAT_ACTION_ID.desc(), constant(AuditActions.Actions.ACTION_ADD_PROFILE.desc())).convertBodyTo(UserProfile.class)
.to(RouterEndPoints.ENDPOINT_USERPROFILE_QUEUE.value()).process(responseProcessor)
.end()
}catch(Exception exc){
ApplicationLogger.error("PMRouteBuilder.configure():Exception while configure the route for 'cxf:bean:process-manager-ws?dataFormat=POJO'",exc);
}
I am able to see the log is getting printed but is there any way to put the debug point(breakpoint) and debug this Route builder?
Additionnaly to the techniques described in the link provided by Claus, we also use the Hawtio console with the Camel plugin.
With this plugin, you can:
Lists of all running Camel applications
Detailed information of each Camel Context such as Camel version number, runtime statics
Lists of all routes in each Camel applications and their runtime statistics
Manage the lifecycle of all Camel applications and their routes, so you can restart / stop / pause / resume, etc.
Graphical representation of the running routes along with real time metrics
Live tracing and debugging of running routes
Profile the running routes with real time runtime statics; detailed specified per processor
Browsing and sending messages to Camel endpoint
I know you asked for Eclipse, but I think debugging step-by-step a DSL is not possible today, that is why we mainly use the Tracer enabled mode and in the last resort, use the Hawtio console for a step-by-step debugging.
Another technique would be to use the JUnit of your IDE but you should modify your class a bit to be better testable:
Use properties for your endpoints, like changing
from("cxf:bean:process-manager-ws?dataFormat=POJO")
...
.to(RouterEndPoints.ENDPOINT_USERPROFILE_QUEUE.value()) // the two instances
with
from("{{from.endpoint}}")
...
.to("{{user.profile.endpoint.1}}")
...
.to("{{user.profile.endpoint.2}}")
and set the properties with the original values in your spring or bluprint file (depending which one you use).
After you can create a test in the test folder (src/test if using maven) called PMRouteBuilderTest with extends CamelTestSupport and with the following content:
#EndpointInject(uri = "direct:test")
protected Endpoint inputTest;
#EndpointInject(uri = "mock:userEndpointOne")
protected MockEndpoint destinationOne;
#EndpointInject(uri = "mock:userEndpointTwo")
protected MockEndpoint destinationTwo;
#Test
public void testRoutingSampleToDestOne() throws Exception {
destinationOne.expectedMessageCount(1);
destinationTewo.expectedMessageCount(1);
String body = "Anything that can make your test useful"
sendBody(inputTest.getEndpointUri(), body);
assertMockEndpointsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new PMRouteBuilder();
}
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
Properties props = new Properties();
// Set your test properties here, those are examples
props.put("from.endpoint", "direct:test");
props.put("user.profile.endpoint.1", "mock:userEndpointOne");
props.put("user.profile.endpoint.2", "mock:userEndpointTwo");
return props;
}
You have to make your test to use the real beans as much as possible but sometimes when you cannot, you have to use a mock framework like Mockito to simulate method calls.
After that, you can execute your test in debug mode from your IDE and put breakpoints to the real processors you're using in the route.
I strongly recommend reading this article about Camel testing.
I postfixed the test class name with Test in sake of simplicity, but normally it should be named PMRouteBuilderIT as it test more than one class and should be executed in the Integration Test phase (mvn verify, with failsafe plugin).
In my project, I have acceptance tests which take a long time to run. When I add new features to the code and write new tests, I want to skip some existing test cases for the sake of time. I am using Spring 3 and junit 4 using SpringJUnit4ClassRunner. My idea is to create an annotation (#Skip or something) for the test class. I am guessing I would have to modify the runner to look for this annotation and determine from system properties if a test class should be included while testing. My question is, is this easily done? Or am I missing an existing functionality somewhere which will help me?
Thanks.
Eric
Annotate your class (or unit test methods) with #Ignore in Junit 4 and #Disabled in Junit 5 to prevent the annotated class or unit test from being executed.
Ignoring a test class:
#Ignore
public class MyTests {
#Test
public void test1() {
assertTrue(true);
}
}
Ignoring a single unit test;
public class MyTests {
#Test
public void test1() {
assertTrue(true);
}
#Ignore("Takes too long...")
#Test
public void longRunningTest() {
....
}
#Test
public void test2() {
assertTrue(true);
}
}
mvn install -Dmaven.test.skip=true
so you can build your project without test,
mvn -Dtest=TestApp1 test
you can just add the name of your application and you can test it.
I use Spring profiles to do this. In your test, autowire in the Spring Environment:
#Autowired
private Environment environment;
In tests you don't want to run by default, check the active profiles and return immediately if the relevant profile isn't active:
#Test
public void whenSomeCondition_somethingHappensButReallySlowly() throws Exception{
if (Arrays.stream(environment.getActiveProfiles()).noneMatch(name -> name.equalsIgnoreCase("acceptance"))) {
return;
}
// Real body of your test goes here
}
Now you can run your everyday tests with something like:
> SPRING_PROFILES_ACTIVE=default,test gradlew test
And when you want to run your acceptance tests, something like:
> SPRING_PROFILES_ACTIVE=default,test,acceptance gradlew test
Of course that's just an example command line assuming you use Gradle wrapper to run your tests, and the set of active profiles you use may be different, but the point is you enable / disable the acceptance profile. You might do this in your IDE, your CI test launcher, etc...
Caveats:
Your test runner will report the tests as run, instead of ignored, which is misleading.
Rather than hard code profile names in individual tests, you probably want a central place where they're all defined... otherwise it's easy to lose track of all the available profiles.
I'm writing some JUnit-based integration tests for a RESTful web service using JerseyTest. The JAX-RS resource classes use Spring and I'm currently wiring everything together with a test case like the following code example:
public class HelloResourceTest extends JerseyTest
{
#Override
protected AppDescriptor configure()
{
return new WebAppDescriptor.Builder("com.helloworld")
.contextParam( "contextConfigLocation", "classpath:helloContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
#Test
public void test()
{
// test goes here
}
}
This works for wiring the servlet, however, I'd like to be able to share the same context in my test case so that my tests can have access to mock objects, DAOs, etc., which seems to call for SpringJUnit4ClassRunner. Unfortunately, SpringJUnit4ClassRunner creates a separate, parallel application context.
So, anyone know how can I create an application context that is shared between the SpringServlet and my test case?
Thanks!
Override JerseyTest.configure like so:
#Override
protected Application configure() {
ResourceConfig rc = new JerseyConfig();
rc.register(SpringLifecycleListener.class);
rc.register(RequestContextFilter.class);
rc.property("contextConfigLocation", "classpath:helloContext.xml");
return rc;
}
For me the SpringServlet was not required, but if you need that you may be able to call rc.register for that too.
I found a couple of ways to resolve this problem.
First up, over at the geek#riffpie blog there is an excellent description of this problem along with an elegant extension of JerseyTest to solve it:
Unit-testing RESTful Jersey services glued together with Spring
Unfortunately, I'm using a newer version of Spring and/or Jersey (forget which) and couldn't quite get it to work.
In my case, I ended up avoiding the problem by dropping the Jersey Test Framework and using embedded Jetty along with the Jersey Client. This actually made better sense in my situation anyway since I was already using embedded Jetty in my application. yves amsellem has a nice example of unit testing with the Jersey Client and embedded Jetty. For Spring integration, I used a variation of Trimbo's Jersey Tests with Embedded Jetty and Spring