I have the following RouteBuilder Class and I am using dependency injection here. How can I test this route builder? I am using JUnit and camel test classes.
public class TestRoute extends RouteBuilder {
private ServiceConfiguration serviceConfiguration;
public TestRoute(ServiceConfiguration serviceConfiguration) {
this.serviceConfiguration = serviceConfiguration;
}
#Override
public void configure() throws Exception {
String incomingURI = serviceConfiguration.getQueueConfiguration().getURI();
String outgoingURI = serviceConfiguration.getHTTPConfiguration().getURI();
from(incomingURI).
setHeader(Exchange.HTTP_METHOD, constant("PUT")).setHeader(Exchange.CONTENT_TYPE, constant("application/json")).
to(outgoingURI);
}
}
My thoughts:
Creating a testConfiguration extending ServiceConfiguration and pass that. But for that I need to set all the configuration and all because the serviceConfiguration contains many other classes. Can I use Mockito here? What is the correct and easy way to test this?
EDIT: I was using the existing route, so that I don't have to write it again. Looks like that is not the correct way of testing in camel. See my test class. Of course, its not working.
public class RabbitMQRouteTest extends CamelTestSupport {
#Mock
ServiceConfiguration serviceConfiguration;
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
System.out.println("testing");
return new TestRoute(serviceConfiguration);
}
#Test
public void testConfigure() throws Exception {
System.out.println("test");
when(serviceConfiguration.getName()).thenReturn("file://target/inbox");
template.sendBodyAndHeader("file://target/inbox", "Hello World",Exchange.FILE_NAME, "hello.txt");
Thread.sleep(1000);
File target = new File("target/outbox/hello.txt");
assertTrue("File not moved", target.exists());
}
}
As you use #Mock, MockitoAnnotations.initMocks has to be invoked. Additionally, when has also be called before passing the reference to TestRoute:
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
// Initialize serviceConfiguration
MockitoAnnotations.initMocks(this);
when(serviceConfiguration.getName()).thenReturn("file://target/inbox");
return new TestRoute(serviceConfiguration);
}
Alternatively to #Mock and MockitoAnnotations.initMocks, just use:
serviceConfiguration = org.mockito.Mockito.mock(ServiceConfiguration.class);
As when is invoked in createRouteBuilder, serviceConfiguration.getName() always returns the same result for all test methods in the JUnit test class. This could be a problem, if different test methods need different URIs.
Alternatively, you may use adviceWith instead as described here.
Camel has two ways to do the route tests, CamelSpringTestSupport and CamelTestSupport.
CamelSpringTestSupport
You can set the route with some camel-whatever-test.xml to setup the route in Spring configuration xml.
CamelTestSupport
You can set the route with RouteBuilder createRoute. And configure the endpoint with mock components.
Here is some reference link, do not forget to add the dependency jars:
http://camel.apache.org/testing.html
Using mockito makes sense in your case as you only need some small parts of the ServiceConfiguration.
On the other hand it is a bad sign that you need it for setting configuration data. You should check if your configuration can be split up into separate parts or not be used in the RouteBuilder at all. If you have one configuration structure that is used in all parts of your code you create a tight coupling between things that should be separate. If you just need the two properties queue uri and http uri in your routebuilder then consider just having two setters for them.
Related
I have a simple camel route:
#Component
public class HelloRoute extends RouteBuilder {
String startEndpoint;
#Override
public void configure() {
from(startEndpoint).process(new HelloProcessor());
}
}
For testing, everything I read says to add a mock endpoint that will store the results:
from(startEndpoint).process(new HelloProcessor()).to("mock:result");
This means I have to change my code to include the mock, and it will run in production. The camel documentatiuon is pretty clear not to use mocks in production:
https://camel.apache.org/mock.html
How do I write a unit test that is using the mock for evaluating the results, but at the same time the router class should run in production without any test code or other artificial and unnecessary endpoint, like
to("log:blah")
Here is what you can do in your test case
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveAddLast().to("mock:result");
}
});
This will add "mock:result" to the end of the route. This way you will be able to modify routes for testing without rewriting them.
Trying to write some proper AEM integration tests using the aem-mocks framework. The goal is to try and test a servlet by calling its path,
E.g. an AEM servlet
#SlingServlet(
paths = {"/bin/utils/emailSignUp"},
methods = {"POST"},
selectors = {"form"}
)
public class EmailSignUpFormServlet extends SlingAllMethodsServlet {
#Reference
SubmissionAgent submissionAgent;
#Reference
XSSFilter xssFilter;
public EmailSignUpFormServlet(){
}
public EmailSignUpFormServlet(SubmissionAgent submissionAgent, XSSFilter xssFilter) {
this.submissionAgent = submissionAgent;
this.xssFilter = xssFilter;
}
#Override
public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
}
}
Here is the corresponding test to try and do the integration testing. Notice how I've called the servlet's 'doPost' method, instead of 'POST'ing via some API.
public class EmailSignUpFormServletTest {
#Rule
public final AemContext context = new AemContext();
#Mock
SubmissionAgent submissionAgent;
#Mock
XSSFilter xssFilter;
private EmailSignUpFormServlet emailSignUpFormServlet;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
Map<String,String> report = new HashMap<>();
report.put("statusCode","302");
when(submissionAgent.saveForm(any(String.class)).thenReturn(report);
}
#Test
public void emailSignUpFormDoesNotRequireRecaptchaChallenge() throws IOException {
// Setup test email value
context.request().setQueryString("email=test.only#mail.com");
//===================================================================
/*
* WHAT I END UP DOING:
*/
// instantiate a new class of the servlet
emailSignUpFormServlet = new EmailSignUpFormServlet(submissionAgent, xssFilter);
// call the post method (Simulate the POST call)
emailSignUpFormServlet.doPost(context.request(),context.response());
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
// assert response is internally redirected, hence expected status is a 302
assertEquals(302,context.response().getStatus());
}
}
I've done a lot of research on how this could be done (here) and (here), and these links show a lot about how you can set various parameters for context.request() object. However, they just don't show how to finally execute the 'post' call.
What you are trying to do is mix a UT with IT so this won't be easy at least with the aem-mocks framework. Let me explain why.
Assuming that you are able to call your required code
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
Your test will end up executing all the logic in SlingAllMethodsServlet class and its parent classes. I am assuming that this is not what you want to test as these classes are not part of your logic and they already have other UT/IT (under respective Apache projects) to cater for testing requirements.
Also, looking at your code, bulk of your core logic resides in following snipper
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
Your UT criteria is already met by the following line of your code:
emailSignUpFormServlet.doPost(context.request(),context.response());
as it covers most of that logic.
Now, if you are looking for proper IT for posting the parameters and parsing them all the way down to doPost method then aem-mocks is not the framework for that because it does not provide it in a simple way.
You can, in theory, mock all the layers from resource resolver, resource provider and sling servlet executors to pass the parameters all the way to your core logic. This can work but it won't benefit your cause because:
Most of the code is already tested via other UT
Too many internal mocking dependencies might make the tests flaky or version dependant.
If you really want to do pure IT, then it will be easier to host the servlet in an instance and access it via HttpClient. This will ensure that all the layers are hit. A lot of tests are done this way but it feels a bit heavy handed for the functionality you want to test and there are better ways of doing it.
Also the reason why context.request().POST doesn't exist is because context.request() for is a mocked state for the sake of testing. You want to actually bind and mock Http.Post operations which needs some way to resolve to your servlet and that is not supported by the framework.
Hope this helps.
I'm porting an app across from JDBC / REST to spring-data-rest and my one and only unit test with fails with the error
NoSuchBeanDefinitionException:
No qualifying bean of type 'com.xxx.repository.ForecastRepository' available
The app was retro-fitted with spring-boot just previously, and now I'm trying to put a new layer in place with spring-data-rest on top of spring-data-jpa.
I'm attempting to work out the correct Java-config according to
Custom Test Slice with Spring Boot 1.4
but I had to deviate from the idiomatic approach because
the #WebMvcTest annotation doesn't suppress the security module which causes the test to fail
the #MockMvcAutoConfiguration fails due to missing dependencies unless I specify #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) (see here)
#WebMvcTest and #SpringBootTest are mutually exclusive since they both specify #BootstrapWith and can't run together
So this is the closest I've got but Spring can't locate my #RepositoryRestResource repository:
Repository
#RepositoryRestResource(collectionResourceRel = "forecasts", path = "forecasts")
public interface ForecastRepository extends CrudRepository<ForecastExEncoded,
Long> {
JUnit Test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK,
classes = {TestRestConfiguration.class})
public class ForecastRestTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private ForecastRepository forecastRepository;
#Before
public void deleteAllBeforeTests() throws Exception {
forecastRepository.deleteAll();
}
#Test
public void shouldReturnRepositoryIndex() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(
jsonPath("$._links.forecasts").exists());
}
}
Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
RepositoryRestMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MockMvcAutoConfiguration.class,
MockMvcSecurityAutoConfiguration.class
})
#Import({PropertySpringConfig.class})
public class TestRestConfiguration {}
Also tried...
I tried to configure the unit test with just #WebMvcTest and this #ComponentScan below from How to exclude AutoConfiguration from Spring Boot), in an attempt to simplify it all, however the excludeFilters had no effect.
#ComponentScan(
basePackages="com.xxx",
excludeFilters = {
#ComponentScan.Filter(type = ASSIGNABLE_TYPE,
value = {
SpringBootWebApplication.class,
JpaDataConfiguration.class,
SecurityConfig.class
})
})
I've set Spring's logging to trace because all I can do at this point is try to find clues as to what is happening from log output. So far though without any luck.
I can see in the logging that RepositoryRestConfiguration is loading, but obviously it isn't fed with the right info and I am unable to work out how that is done, after googling and pouring over the Spring docs and API. I think I must have read every relevant question here on SO .
Update 2016-11-16 10:00
One thing I see in the logs which concerns me is this:
Performing dependency injection for test context [DefaultTestContext#2b4a2ec7 [snip...]
classes = '{class com.xxx.TestRestConfiguration,
class com.xxx.TestRestConfiguration}',
i.e. the context lists the configuration class twice. I specified the config class (once only) on the #SpringBootTest#classes annotation. But if I leave off the #classes from the annotation, Spring Boot finds and pulls in all the config via the #SpringBootApplication class.
So is that a hint that I am specifying the configuration in the wrong place? How else would I do it?
After way too much time, I settled on this approach.
Custom Test Slice with Spring Boot 1.4 looked promising but I couldn't get anywhere with it.
While going over and over
Accessing JPA Data with REST
I realised I had to include the JPA setup because spring-data-rest is using them directly - no chance to mock them or run unit tests without an embedded database.
At least not as far as I understand it. Maybe it is possible to mock them and have spring-data-rest run on the mocks against test data, but I think spring-data-rest and spring-data are probably too tightly coupled.
So integration testing it must be.
In the Spring source code provided with the articles above
gs-accessing-data-rest/ApplicationTests.java
the logging shows Spring Boot pulling in the whole configuration for the application context.
So that my SpringBootApplication class is avoided and the security module isn't loaded up, I set up my tests like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = {
JpaDataConfiguration.class,
TestJpaConfiguration.class,
TestRestConfiguration.class,
PropertySpringConfig.class})
public class ForecastRestTests {
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
private MockMvc mockMvc;
#Autowired
private ForecastRepository forecastRepository;
#Before
public void deleteAllBeforeTests() throws Exception {
forecastRepository.deleteAll();
}
#Test
public void shouldReturnRepositoryIndex() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(
jsonPath("$._links.forecasts").exists());
}
}
with these configuration classes:
#Configuration
#EnableJpaRepositories(basePackages = {"com.bp.gis.tardis.repository"})
#EntityScan(basePackages = {"com.bp.gis.tardis.type"})
public class JpaDataConfiguration {
and
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
CacheAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
TransactionAutoConfiguration.class,
TestDatabaseAutoConfiguration.class,
TestEntityManagerAutoConfiguration.class })
public class TestJpaConfiguration {}
and
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
RepositoryRestMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MockMvcAutoConfiguration.class,
MockMvcSecurityAutoConfiguration.class
})
public class TestRestConfiguration {}
so the TL;DR summary is: use #ContextConfiguration to specify the configuration files that specify #OverrideAutoConfiguration and #ImportAutoConfiguration
I want to unit test a java class with an autowired final class object, as well as another autowired class that has #PostConstruct method. While it is possible to test them individually, i am not able to combine them together.
This question is an extension to the question on injecting mockito mocks into spring bean
Code to be tested
public class A {
#Autowired
private FinalClass serviceClient;
#Autowired
private ClassWithPostConstructor resourceVerifier;
//no setters or constructors
public String useBothFinalClassAndClassWithPostConstructor() {
//logic to be tested
}
}
Working Test class
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClass.class)
public class ATest {
//#org.mockito.Mock //fails to mock final class
#org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
#org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
//#InjectMocks //fails since mocking final class
private A a;
#Before
public void init() {
a = new A();
//working snippet with setters created in A and without #Autowired here within the test
serviceClient = PowerMock.create(FinalClass.class);
a.setServiceClient(serviceClient);
resourceVerifier = PowerMock.create(ClassWithPostConstructor.class);
a.setClassWithPostConstructor(resourceVerifier);
}
#Test
public void testTheMethodUsingExpectAndVerify() {
//test the functionality here
EasyMock.expect(serviceClient.callService()).andReturn("someMock");
EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
PowerMock.replayAll();
A.useBothFinalClassAndClassWithPostConstructor();
PowerMock.verifyAll();
}
}
The above code works with the need for setters in file
Expected Test class
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:spring-configuration/unit-testing-config.xml"})
#PrepareForTest(FinalClass.class)
public class ATest {
#Autowired
private FinalClass serviceClient;
#Autowired
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
private A a;
#Before
public void init() {
a = new A();
}
#Test
public void testTheMethodUsingExpectAndVerify() {
//test the functions here
EasyMock.expect(serviceClient.callService()).andReturn("someMock");
EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
PowerMock.replayAll();
A.useBothFinalClassAndClassWithPostConstructor();
PowerMock.verifyAll();
}
}
//spring-configuration/unit-testing-config.xml
//same error even on customer factory
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
<constructor-arg type="java.lang.Class" value="com.company...resourceVerifier" />
</bean>
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
<constructor-arg type="java.lang.Class" value="com.company...serviceClient" />
</bean>
The above snippet mocks finalClass but calls #PostConstructor of ResourceVerifier.class - What should be done here to overcome this call.
Investigations
It is possible to test autowired files using #InjectMocks without the need for spring context configurations.
#InjectMock fails silently for static and final fields and when failing, it doesn't inject other mocks as well.
It is possible to mock final class using PowerMock's createMock and run the test with PowerMockRunner and #PrepareForTest. But this requires new unnecessary setters to inject the mocks for #Autowired fields.
MockitoAnnotations.#Mock doesn't work along well with PowerMock (especially when mocking final class objects) and can be solved via EasyMock.Annotations.#Mock
EasyMock and PowerMock doesn't have an #InjectMocks annotation to inject the mocks as possible by Mockito (Would have solved the problem in secs).
It is possible to inject autowired spring beans via SpringJUnit4Runner and a separate unit-testing #ContextConfiguration
It is possible to run the same test file with both PowerMockRunner and SpringJUnit4Runner with PowerMockRunnerDelegate
I know that #PostConstruct method will not be executed automatically if mocked in code than by using spring bean creation and injection.
If a wrapper factory bean class is written and used to create the mocks, it injects automatically, but calls the #PostConstruct method as well along with it.
It is not possible to depend on Springockito since it is unreliable at this stage.
But none of these worked since the usecase is a combination of all these.
Possible Solutions
Remove #Autowired fields and use Setter injections so that it is possible by mocking normally using PowerMock(Tested to work) - But it is a convention followed by external team packages - i should try my best to stick to it.
Or set #Autowired to the setters or to constructor
Alternatives?
I don't feel that the classes require reorganisation as they serve their purposes and are well designed as well.
Any other means that doesn't require to keep hands on the class under test - What if i didn't have the permissions to modify this class? i.e., a pure testing library dependent solution.
Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.
Hmm,
Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.
Seems to me you have a mess in your head and misunderstanding PowerMock/Mockito and EasyMock.
You should never use at same time PowerMockito and PowerMock, because these two classes are PowerMock friendly API for two different mocking frameworks: EasyMock and Mockito. And there is no reason to use they both.
And of course this want work
//#org.mockito.Mock //fails to mock final class
#org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
#org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
//#InjectMocks //fails since mocking final class
private A a;
Because, you decelerate and create mocks via EasyMock API, but tries to inject it with Mockito Annotation.
You have to choose only one Mocking Framework and use suitable API for it. For now, Mockito + PowerMockito (PowerMock API for Mockito) better fit your requirements.
You may full example how it works on PowerMock github
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClass.class)
public class SpringInjectFinalClassExampleTest {
#Mock
private FinalClass finalClass;
#InjectMocks
private MyBean myBean = new MyBean();;
#Test
public void testInjectFinalClass() {
final String value = "What's up?";
when(finalClass.sayHello()).thenReturn(value);
assertEquals(value, myBean.sayHello());
}
}
I have inherited some code that isn't tested and which loads a resource using a method like :
SomeClass.class.getClassLoader().getResource("somefile");
I've written the following test but there are 0 interactions with the Mock class loader I've created. Can anyone comment on whether this type of test is possible.
public enum SomeClass {
INSTANCE;
public boolean someMethod() {
URL pathToLicense = SomeClass.class.getClassLoader().getResource("somefile");
return false;
}
}
#Test
public void testLicenseWorkflow(){
ClassLoader cl = PowerMockito.mock(ClassLoader.class);
File f = new File("someFile");
assertTrue(f.exists());
logger.info(f.getCanonicalPath() );
when(cl.getResource("somefile")).thenReturn(f.toURL());
verify(cl).getResource("somefile");
assertTrue(SomeClass.INSTANCE.someMethod());
}
Update - Adding a resources via Classloader
I've also tried the following but the someMethod this doens't seem to work either
new URLClassLoader(((URLClassLoader) SomeClass.INSTANCE.getClass().getClassLoader()).getURLs()) {
#Override
public void addURL(URL url) {
super.addURL(url);
logger.info("Calling add URL");
}
}.addURL(f.toURI().toURL());
You are not passing cl to anything. You prepare a mock for a classloader but then proceed to load the resource with another classloader, the one that loaded SomeClass. That is why you have 0 interactions in your mock.
And about your first question, it is possible if somehow you pass your mocked classloader to the method that actually loads the resource. For example, something like
public boolean someMethod(Classloader loader) {
URL pathToLicense = loader.getResource("somefile");
return false;
}
But I have to say that IMO, this test is not very useful, you should be mocking your own components, not java classes. If your goal mocking the classloader is to inject a different file when testing, a better approach is to change your code to receive a Stream and inject a stream connected to the file in production and in testing inject a stream connected to an element in memory.
In other words, resources make for bad testing when they need to be changed at test time