I've probably completly missed something but I can't manage to test my route as I want to.
I've got the following bean :
#Component("fileProcessor")
public class FileProcessor {
public boolean valid(#Header("customObject) CustomObject customObject,Exchange exchange) throws IOException{
return false;
}
I've a route calling my bean like so :
from("direct:validationFile").routeId("validationFile").validate().method("fileProcessor","valid")
// Other stuff
.end();
Here is my unit test, based on a example I found:
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class})
#ContextConfiguration(locations = { "classpath:/tu-dao-beans.xml" })
public class FileProcessorTest extends CamelTestSupport {
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Override
public boolean isDumpRouteCoverage() {
return true;
}
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
#Test
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
// from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
from("direct:start").routeId("validationFile").validate().method("fileProcessor","valid").to("mock:result");
}
};
}
}
The test fails because fileProcessor is not found, yet I'm pretty sure my spring contextis properly loaded, I'm using the same beans.xmlfile for my dbunit tests and my DAO components are found just fine... What am I missing ?
EDIT:
Thanks to Jérémis B's answer I fixed my problem easily. In case someone stumble as I did here is the code I added:
#Autowired
private FileProcessor fileProcessor;
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry registry = super.createRegistry();
registry.bind("fileProcessor", fileProcessor);
return registry;
}
You can see the official documentation for an "How to" test with Spring.
In your example, you create a Spring Context, but use the CamelTestSupport : This class create a CamelContext which is not aware of the Spring Context. The bean "fileProcessor" is not seen by this context.
There is a lot of ways to do this kind of test. The easiest, with the code you already have, is maybe to:
Inject the fileProcessor in your test class, with #Autowire
Override createRegistry and add the fileProcessor to the registry
You can too override CamelSpringTestSupport and implement createApplicationContext. Another way is to keep the route definition in a Spring Bean (through xml, or a RouteBuilder), and inject in your test MockEndpoints or ProducerTemplate.
Related
Is it possible to write unit test using Junit 5 mockito for retryable annotations?
I am having a service interface which has only one method, which downloads the file from remote url
#service
interface downloadpdf{
#Retryable(value = { FileNotFoundException.class, HttpClientErrorException.class }, maxAttempts = 5, backoff = #Backoff(delay = 1000))
public string downloadpdffile(string remoteurl, string pdfname);
}
I have tried referring sites and found using Spring4JunitRunner implementation to test retry. Got confused with implementation. Is it possible to write unit test using Junit 5 mockito for retryable annotations?. Could you please elaborate on the solution here?
You need to use #SpringJUnitConfig (which is the equivalent of the JUnit4 runner). Or #SpringBootTest as you are using Boot.
#Retryable only works with beans managed by Spring - it wraps the bean in a proxy.
#SpringBootApplication
#EnableRetry
public class So71849077Application {
public static void main(String[] args) {
SpringApplication.run(So71849077Application.class, args);
}
}
#Component
class RetryableClass {
private SomeService service;
void setService(SomeService service) {
this.service = service;
}
#Retryable
void retryableMethod(String in) {
service.callme();
throw new RuntimeException();
}
#Recover
void recover(Exception ex, String in) {
service.failed();
}
}
interface SomeService {
void callme();
void failed();
}
#SpringBootTest
class So71849077ApplicationTests {
#MockBean
SomeService service;
#Test
void testRetry(#Autowired RetryableClass retryable) {
SomeService service = mock(SomeService.class);
retryable.setService(service);
retryable.retryableMethod("foo");
verify(service, times(3)).callme();
verify(service).failed();
}
}
I was also trying to implement this using Junit5.
Tried various options but that didn't help. Then after googling for few hours, got the following link and it helped to succeed.
https://doctorjw.wordpress.com/2022/04/29/spring-testing-a-single-bean-in-junit-5-springextension/
Reference code below, for detailed explanation, please refer the blog.
#Component
public class MyClass {
private ObjectMapper objectMapper;
private RestTemplate restTemplate;
#Value("${testValue:5}")
private int value;
#Retryable(....)
public void doStuff() throws SomeException {
...
}
}
What I’ve discovered is, if I declare my test class this way:
#ExtendWith( SpringExtension.class )
#Import( { MyClass.class, ObjectMapper.class } )
#EnableRetry
public class MyClassTest {
#Autowired
private MyClass myClass;
#MockBean
private RestTemplate restTemplate;
#Autowired
private ObjectMapper objectMapper;
#BeforeEach
public void setup() {
// If we are going to jack with the object configuration,
// we need to do so on the actual object, not the Spring proxy.
// So, use AopTestUtils to get around the proxy to the actual obj.
TestingUtils.setFieldValue( AopTestUtils.getTargetObject( myClass ), "value", 10 );
}
}
You will notice the inclusion of 1 other class, TestingUtils.class. This class looks like:
public class TestingUtils {
public static void setFieldValue( Object object, String fieldName, Object value ) {
Field field = ReflectionUtils.findField( object.getClass(), fieldName );
ReflectionUtils.makeAccessible( field );
ReflectionUtils.setField( field, object, value );
}
}
All credits goes to the author of the blog.
Suppose I have Camel route like this:
#Component
public class MyRoute extends RouteBuilder {
private final BrokenBean brokenBean;
public MyRoute(BrokenBean brokenBean) {
this.brokenBean = brokenBean;
}
#Override
public void configure() throws Exception {
from("{{rabbitmq.inbound}}")
.errorHandler(defaultErrorHandler()
.maximumRedeliveries(1)
.redeliveryDelay(1000))
.end()
.onCompletion()
.onCompleteOnly()
.to("direct:success")
.end()
.onCompletion()
.onFailureOnly()
.to("direct:failure").id("failure")
.end()
.routeId("my_route")
.bean(brokenBean, "hello")
.to("direct:success").id("success");
from("direct:success")
.log("Success received");
from("direct:failure")
.log("Failed received");
}
And here is bean logic which is called from this route.
This is just an example.
#Component
public class BrokenBean {
public void hello() {
System.out.println("Hello called");
}
}
As route logic reveals and I tested it manually, if we got the exception
from BrokeBean the message would be routed to direct:failure and it does in runtime.
But in test below:
#RunWith(CamelSpringBootRunner.class)
#ContextConfiguration(classes = MyRouteTest.TestConfig.class)
public class MyRouteTest {
#Produce("direct:inbound")
protected ProducerTemplate directInbound;
#Autowired
SpringCamelContext context;
#MockBean
BrokenBean brokenBeanMock;
#Before
public void setUp() throws Exception {
AdviceWith.adviceWith(context.getRouteDefinition("my_route"),
context,
new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
onException(RuntimeException.class)
.continued(true);
weaveById("failure").replace().to("mock:direct:failure");
weaveById("success").replace().to("mock:direct:success");
}
});
}
#Test
public void testMyRouteFailedSuccessExpected() throws Exception {
MockEndpoint mockEndpoint = context.getEndpoint("mock:direct:failure",
MockEndpoint.class);
doThrow(new RuntimeException("Failed call")).when(brokenBeanMock).hello();
directInbound.sendBody("hello there");
mockEndpoint.expectedMessageCount(1);
mockEndpoint.assertIsSatisfied();
}
#Test
public void testMyRouteSuccessFailedExpected() throws Exception {
MockEndpoint mockEndpoint = context.getEndpoint("mock:direct:success",
MockEndpoint.class);
doThrow(new RuntimeException("Failed call")).when(brokenBeanMock).hello();
directInbound.sendBody("hello there");
mockEndpoint.expectedMessageCount(1);
mockEndpoint.assertIsSatisfied();
}
#Configuration
#Import({MyRoute.class})
public static class TestConfig extends CamelConfiguration {
#Bean
public BridgePropertyPlaceholderConfigurer bridgePropertyPlaceholderConfigurer() {
final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
final BridgePropertyPlaceholderConfigurer configurer = new BridgePropertyPlaceholderConfigurer();
yaml.setResources(new ClassPathResource("application.yml"));
configurer.setOrder(1);
configurer.setIgnoreUnresolvablePlaceholders(true);
configurer.setProperties(yaml.getObject());
return configurer;
}
}
}
I have the opposite result: testMyRouteSuccessFailedExpected succeeded and testMyRouteFailedSuccessExpected failed.
Which is not what I expected. Actually I tried a lot with adviceWith setup but with no luck.
It seems a simple case to check but behaviour look strange to me.
Any help appreciated. Thanks.
You can essentially only use 1 onCompletion in a route. You have 2. However we don't validate this on startup and report a problem.
Camel cannot understand that your 2 on completions would not overlap as one is for success and another for failure. So you need to only use 1 in your route.
I try to make a junit test for apache camel route.
Something like this :
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(
loader = CamelSpringDelegatingTestContextLoader.class
)
public class MyExportRouteBuilderIT extends CamelTestSupport {
#Test
public void test() {
// trigger and check the files made by route builder processor
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new MyExportRouteBuilder();
}
}
The builder class is defined like this
from("quartz2://exportJob?cron=" + cronTrigger)
.setHeader(FILE_NAME, expression(FILE_NAME_FORMAT))
.process(myExportRouteProcessor)
.marshal(new BindyCsvDataFormat(MyExportData.class))
.to("file:///destination);
The 'myExportRouteProcessor' class just gets some data from the JPA repository and puts the results to the route.
What I want is to trigger this route in the test class to check if the whole process was properly finished.
Currently, processor is not fired. What should I do more ?
You can replace quartz2 component in your test with direct using AdviceWithRouteBuilder#replaceFromWith.
#Test
public void test() throws Exception{
//mock input route (replace quartz with direct)
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:triggerQuartz");
}
});
//trigger endpoint
sendBody("direct:triggerQuartz", null);
//do some assertions
}
This might have been coded wrongly, but any idea how it should be done is appreciated.
I have this class TestClass which needs to inject many service class. Since I can't use #BeforeClass on #Autowired objects, I result on using AbstractTestExecutionListener. Everything was working as expected but when I'm on #Test blocks, all objects are evaluated null.
Any idea how to solve this?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { ProjectConfig.class })
#TestExecutionListeners({ TestClass.class })
public class TestClass extends AbstractTestExecutionListener {
#Autowired private FirstService firstService;
// ... other services
// objects needs to initialise on beforeTestClass and afterTestClass
private First first;
// ...
// objects needs to be initialised on beforeTestMethod and afterTestMethod
private Third third;
// ...
#Override public void beforeTestClass(TestContext testContext) throws Exception {
testContext.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(this);
first = firstService.setUp();
}
#Override public void beforeTestMethod(TestContext testContext) throws Exception {
third = thirdService.setup();
}
#Test public void testOne() {
first = someLogicHelper.recompute(first);
// ...
}
// other tests
#Override public void afterTestMethod(TestContext testContext) throws Exception {
thirdService.tearDown(third);
}
#Override public void afterTestClass(TestContext testContext) throws Exception {
firstService.tearDown(first);
}
}
#Service
public class FirstService {
// logic
}
For starters, having your test class implement AbstractTestExecutionListener is not a good idea. A TestExecutionListener should be implemented in a stand-alone class. So you might want to rethink that approach.
In any case, your current configuration is broken: you disabled all default TestExecutionListener implementations.
To include the defaults, try the following configuration instead.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ProjectConfig.class)
#TestExecutionListeners(listeners = TestClass.class, mergeMode = MERGE_WITH_DEFAULTS)
public class TestClass extends AbstractTestExecutionListener {
// ...
}
Regards,
Sam (author of the Spring TestContext Framework)
I have a interface here
interface Idemo{
public int getDemo(int i);
}
And it's one implementation
class DemoImpl implements Idemo{
#Override
public int getDemo(int i){
return i+10;
}
}
And there is a class which has a dependency on Idemo
class Sample{
#Inject
Idemo demo;
public int getSample(int i){
return demo.getDemo(i);
}
}
Now say I want to test Sample class
public class SampleTest extends JerseyTest {
#Inject
Sample s;
#Override
protected Application configure() {
AbstractBinder binder = new AbstractBinder() {
#Override
protected void configure() {
bind(Demo.class).to(Idemo.class);
bind(Sample.class).to(Sample.class); //**doesn't work**
}
};
ResourceConfig config = new ResourceConfig(Sample.class);
config.register(binder);
return config;
}
#Test
public void test_getSample() {
assertEquals(15, s.getSample(5)); //null pointer exception
}
}
Here the Sample instance is not getting created and s remains null.I suppose this is because by the time the execution reaches line where binding is specified this test class has already been created.But I am not sure.With Spring Autowired instead of jersey CDI the same works
Had Sample been a resource/controller class the test framework would create an instance of it with no need to inject it but is it possible to test any other non-web class using Jersey DI ?
The reason it works with Spring is that the test class is managed by the Spring container by using #RunWith(SpringJUnit4ClassRunner.class). The runner will inject all managed objects into the test object. JerseyTest is not managed this way.
If you want, you can create your own runner, but you need to understand a bit how HK2 (Jersey's DI framework) works. Take a look at the documentation. Everything revolves around the ServiceLocator. In a standalone, you might see something like this to bootstrap the DI container
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create(null);
ServiceLocatorUtilities.bind(locator, new MyBinder());
Then to get the service, do
Service service = locator.getService(Service.class);
In the case of the test class, we don't need to gain any access to the service object, we can simply inject the test object, using the ServiceLocator:
locator.inject(test);
Above, test is the test class instance that gets passed to us in our custom runner. Here is the example implementation of a custom runner
import java.lang.annotation.*;
import org.glassfish.hk2.api.*;
import org.glassfish.hk2.utilities.*;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.*;
public class Hk2ClassRunner extends BlockJUnit4ClassRunner {
private final ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
private Class<? extends Binder>[] binderClasses;
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public static #interface Binders {
public Class<? extends Binder>[] value();
}
public Hk2ClassRunner(Class<?> cls) throws InitializationError {
super(cls);
Binders bindersAnno = cls.getClass().getAnnotation(Binders.class);
if (bindersAnno == null) {
binderClasses = new Class[0];
}
}
#Override
public Statement methodInvoker(FrameworkMethod method, final Object test) {
final Statement statement = super.methodInvoker(method, test);
return new Statement() {
#Override
public void evaluate() throws Throwable {
ServiceLocator locator = factory.create(null);
for (Class<? extends Binder> c : binderClasses) {
try {
ServiceLocatorUtilities.bind(locator, c.newInstance());
} catch (InstantiationException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
locator.inject(test);
statement.evaluate();
locator.shutdown();
}
};
}
}
In the runner, the methodInvoker is called for every test method, so we are creating a fresh new set of objects for each test method called.
Here is a complete test case
#Binders({ServiceBinder.class})
#RunWith(Hk2ClassRunner.class)
public class InjectTest {
public static class Service {
#Inject
private Demo demo;
public void doSomething() {
System.out.println("Inside Service.doSomething()");
demo.doSomething();
}
}
public static class Demo {
public void doSomething() {
System.out.println("Inside Demo.doSomething()");
}
}
public static class ServiceBinder extends AbstractBinder {
#Override
protected void configure() {
bind(Demo.class).to(Demo.class);
bind(Service.class).to(Service.class);
}
}
#Inject
private Service service;
#Test
public void testInjections() {
Assert.assertNotNull(service);
service.doSomething();
}
}
I was facing the same situation but in the context of running some integrations test that needs to have some of the singletons that my application have already defined.
The trick that I found is the following. You just need to create a normal test class or a standalone that use the DropwizardAppRule
In my case, I use JUnit as I was writing some integration test.
public class MyIntegrationTest{
//CONFIG_PATH is just a string that reference to your yaml.file
#ClassRule
public static final DropwizardAppRule<XXXConfiguration> APP_RULE =
new DropwizardAppRule<>(XXXApplication.class, CONFIG_PATH);
}
The #ClassRule will start your application like is said here . That
means you will have access to everything and every object your application needs to start. In my case, I need to get access to a singleton for my service I do that using the #Inject annotation and the #Named
public class MyIntegrationTest {
#ClassRule
public static final DropwizardAppRule<XXXConfiguration> APP_RULE =
new DropwizardAppRule<>(XXXAplication.class, CONFIG_PATH);
#Inject
#Named("myService")
private ServiceImpl myService;
}
Running this will set to null the service as #Inject is not working because we don't have at this point anything that put the beans into the references. There is where this method comes handy.
#Before
public void setup() {
ServiceLocator serviceLocator =((ServletContainer)APP_RULE.getEnvironment().getJerseyServletContainer()).getApplicationHandler().getServiceLocator();
//This line will take the beans from the locator and inject them in their
//reference, so each #Inject reference will be populated.
serviceLocator.inject(this);
}
That will avoid creating other binders and configurations outside of the existing on your application.
Reference to the ServiceLocator that DropwizardAppRule creates can be found here