I have a File handler for Spring Batch that I want to test.
SpringApplication.run() is a static method for which I would like to verify the arguments passed to it.
Does this mean I need to go down the PowerMock path or is there something in the SpringFramework that will enable me to test this?
public File handleFile(File file) {
// Start the Batch Process and set the inputFile parameter
String[] args = {"--inputFile=" + file.getAbsolutePath()};
SpringApplication.run(InitialFileBatchApplication.class, args);
return null;
}
My test class has the following annotations which don't seem to be working:
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#SpringBootTest
#PrepareForTest(SpringApplication.class)
What am I missing?
The exception getting thrown is:
java.lang.IllegalStateException: Failed to transform class with name org.springframework.boot.SpringApplication. Reason: cannot find
org.springframework.web.context.support.StandardServletEnvironment
This occurs when the #PrepareForTest(SpringApplication.class) is processed. I'm testing a Spring Batch application so there is no web environment and I've also added.
#SpringBootTest(webEnvironment=WebEnvironment.NONE)
As I share your dislike for PowerMock, the first answer is unfortunately: the method that you have written right now - yes that can only be tested using PowerMock.
So, if you want to test that method; you have to use PowerMock. Or you take the minimal risk ... and simply don't test it.
Beyond that: I recommend to put that method into some interface; you simply want to prevent that this static call gives you trouble when you start testing other methods that want to call handleFile() - then you want to be able to mock that call; to prevent that static call inside to happen.
This issue due to the exception that I was having was due to a missing entry in the pom.xml, which frustrates me a bit with the SpringFramework since I'm working only in a batch application and have no web or servlet components whatsoever in this test. The missing pom entry was.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
The other spring dependecies that I had were
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
In order to test this, I did take the approach of PowerMock with externalizing some of the methods so that I could test them and even though I'm testing with a Spring Application, I was able to exclude the SpringRunner that loads the context to simplify this test. Below is my implementation class as well as the test class that tested it.
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
public class InitialFileInputFileHandler {
private Logger logger = LoggerFactory.getLogger(InitialFileInputFileHandler.class);
/**
* Handles the Initial Client files that get put into the input directory that match the pattern
* defined in initialFileListenerApplicationContext.xml
* #param file - The file
* #return
*/
public File handleFile(File file) {
logger.info("Got the Initial Client file: " + file.getAbsolutePath() + " start Batch Processing");
// Start the Batch Process and set the inputFile parameter
String[] args = buildArguments(file);
SpringApplication.run(InitialFileBatchApplication.class, args);
// Whatever we return is written to the outbound-channel-adapter.
// Returning null will not write anything out and we do not need an outbound-channel-adapter
return null;
}
protected String[] buildArguments(File file) {
String[] args = {"--inputFile=" + file.getAbsolutePath()};
return args;
}
}
And here's the test class
import static org.mockito.Mockito.*;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.MatcherAssert.*;
import java.io.File;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;
// This test class must test static methods. One way to do that is with PowerMock.
// Testing with static methods so we have to run with the PowerMockRunner.
#RunWith(PowerMockRunner.class)
// The static method that we want to test is in the SpringApplication class so
// by using PowerMock we have to prepare this class for testing.
#PrepareForTest({SpringApplication.class})
// If you wanted to load a SpringContext you'd have to include the SpringRunner.
// Since our Runner is PowerMockRunner, we still have to setup the spring context, so
// you setup the SpringRunner as the delegate.
//#PowerMockRunnerDelegate(SpringRunner.class)
public class InitialFileInputFileHandlerTest {
// Setup a mockFile so that I can specify what comes back from the getAbsolutiePath method
// without actually to have a file on the file system.
#Mock File mockFile;
private InitialFileInputFileHandler handler;
#Before
public void setUp() throws Exception {
handler = new InitialFileInputFileHandler();
org.mockito.Mockito.when( mockFile.getAbsolutePath() ).thenReturn("src/input/fooFile.txt");
}
#Test
public void testBuildArguments(){
String[] args = handler.buildArguments(mockFile);
assertThat( args[0], equalTo("--inputFile=src/input/fooFile.txt") );
}
#Test
public void testHandleFile() throws Exception {
// Tell PowerMockito to keep track of my static method calls in the SpringApplication class
PowerMockito.mockStatic( SpringApplication.class );
// What I expect the argument to be
String[] args = {"--inputFile=src/input/fooFile.txt"};
// Call the actual method
handler.handleFile(mockFile);
// Have to call verifyStatic since its a static method.
PowerMockito.verifyStatic();
// One of a few possibilities to test the execution of the static method.
//SpringApplication.run( InitialFileBatchApplication.class, args);
//SpringApplication.run( Mockito.any(InitialFileBatchApplication.class), eq(args[0]));
SpringApplication.run( Mockito.any(Object.class), eq(args[0]));
}
}
1.if you want to verify args in your tests, you need to return it to caller code of method handleFile(file) and currently you are doing - return null; , instead you should return args ( if method signature can be changed ).
I have assumed that handleFile method is in InitialFileBatchApplication class.
#Test
public void testHandleFile() {
File file = new File("ABC");
String[] response = new InitialFileBatchApplication().handleFile(file);
//Verify response here
}
Above will actually kick off your job.
2.If you wish to mock - SpringApplication.run as it is , PowerMock is the way to go. You should indicate in question as what error you are getting with current set up.
3.Mockito is inbuilt in Spring Test now so if you can refactor your code to have a non - static method call the static method then you can mock non - static method and that will eventually mock your static call. #MockBean annotation is part of Spring Test.
4.If Mocking SpringApplication.run in spring batch is equivalent to not running the job but simply initializing the context then purpose can be achieved by saying , spring.batch.job.enabled=false in application.properties. Only that your unit tests will have to wait for real call to - SpringApplication.run to complete but job will not kick off.
Code refactoring is always encouraged to make your code unit testable in addition to functionally being correct so don't hesitate to refactor to overcome framework limitations.
Hope it helps !!
Related
I am new to Spock unit testing framework. I am able to write unit test cases using Spock for simple logics. Now, I am writing the same for a Rest API.
I am using Spring's RestTemplate to hit the GET and POST requests. I have seen number of example in google for GET request(like WireMock). But, there is no enough information to give clarification on how to write test cases for POST requests.
Here is my sample code which is written in Junit. I have to convert the same to Spock (I can do).
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
#RunWith(SpringJUnit4ClassRunner.class)
#AutoConfigureMockMvc
#ActiveProfiles(value = "integration")
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class LocalControllerTest {
#Rule
public WireMockRule wireMockRule = new WireMockRule(9999);
#Before
public void setUp() {
mockRemoteService();
}
#Test
public void testLocalServiceWithMockedRemoteService() throws Exception {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:8080/localService", String.class);
org.junit.Assert.assertEquals("Input : request from client endpoint, Message : mocked remote service response", response.getBody());
}
private void mockRemoteService() {
stubFor(get(urlEqualTo("/remote"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("remoteServiceResponse.json")));
}
}
Main issue is, even though I wrote the test cases, the call is actually happening to the service (Which is at my localhost) which I am not expecting to happen it. Is there any way to Mock up the data locally and make the call to dummy endpoint for POST request using Spock framework? I don't know much about to mock a dummy endpoint.
Your help is highly appreciated.
Thanks.
How can I run code in my #RunWith(SpringRunner.class) #SpringBootTest(classes = {...}) JUnit test before Spring starts?
This question has been asked several times (e.g. 1, 2) but was always "solved" by some configuration recommendation or other, never with a universal answer. Kindly don't question what I am about to do in that code but simply suggest a clean way to do it.
Tried so far and failed:
Extend SpringJUnit4ClassRunner to get a class whose constructor can run custom code before initializing Spring. Failed because super(testClass) must be called first thing and already does a whole lot of things that get in the way.
Extend Runner to get a class that delegates to SpringRunner instead of inheriting it. This class could run custom code in its constructor before actually instantiating the SpringRunner. However, this setup fails with obscure error messages like java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig. "Obscure" because my test has no web config and thus shouldn't meddle with sessions and cookies.
Adding an ApplicationContextInitializer that is triggered before Spring loads its context. These things are easy to add to the actual #SpringApplication, but hard to add in Junit. They are also quite late in the process, and a lot of Spring has already started.
One way to do it is to leave out SpringRunner and use the equivalent combination of SpringClassRule and SpringMethodRule instead. Then you can wrap the SpringClassRule and do your stuff before it kicks in:
public class SomeSpringTest {
#ClassRule
public static final TestRule TestRule = new TestRule() {
private final SpringClassRule springClassRule =
new SpringClassRule();
#Override
public Statement apply(Statement statement, Description description) {
System.out.println("Before everything Spring does");
return springClassRule.apply(statement, description);
}
};
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Test
public void test() {
// ...
}
}
(Tested with 5.1.4.RELEASE Spring verison)
I don't think you can get more "before" than that. As for other options you could also check out #BootstrapWith and #TestExecutionListeners annotations.
Complementing jannis' comment on the question, the option to create an alternative JUnit runner and let it delegate to the SpringRunner does work:
public class AlternativeSpringRunner extends Runner {
private SpringRunner springRunner;
public AlternativeSpringRunner(Class testClass) {
doSomethingBeforeSpringStarts();
springRunner = new SpringRunner(testClass);
}
private doSomethingBeforeSpringStarts() {
// whatever
}
public Description getDescription() {
return springRunner.getDescription();
}
public void run(RunNotifier notifier) {
springRunner.run(notifier);
}
}
Being based on spring-test 4.3.9.RELEASE, I had to override spring-core and spring-tx, plus javax.servlet's servlet-api with higher versions to make this work.
I am working on an API for work, we use a shared library for multiple projects for the purposing of our logging framework. The class used uses all static methods for its calls.
I am trying to Unit test an API call, I can not have it call anything on the Logging class, else it will fail.
I have tried using Powermock, but it fails on
PowerMockito.mockStatic(LoggingFramework.class);
Mockito.when(LoggingFramework.startACall(anyString())).thenReturn("someTimestamp");
returning a
ClassCastException: org.apache.logging.slf4j.SLF4JLoggerContext cannot be cast to org.apache.logging.log4j.core.LoggerContext
the line in LoggingFramework that throws it, is inside a static initializer block outside of any methods in the class.
In order to suppress static initialization you should use #SuppressStaticInitializationFor. So your code will look like this:
#RunWith(PowerMockRunner.class)
#SuppressStaticInitializationFor("so.LoggingFramework") //here goes fully-qualified name of a class
public class LoggingFrameworkTest {
#Test
public void test() {
//given:
PowerMockito.mockStatic(LoggingFramework.class);
Mockito.when(LoggingFramework.foo(anyString())).thenReturn("stub");
//when:
String foo = LoggingFramework.foo("ignored");
//then:
PowerMockito.verifyStatic(LoggingFramework.class, Mockito.times(1));
LoggingFramework.foo(anyString()); //two-step verification of a static method
assertThat(foo, equalTo("stub"));
}
}
Verification of a static method is performed in two steps. It is explained here
I followed the https://learn.microsoft.com/en-us/azure/application-insights/app-insights-java-get-started, but still without success.
I have applicationinsights-web dependency in place via maven
I added ApplicationInsights.xml to main/resources with hardcoded instrumentation key and even with <SDKLogger /> inside
I added the scan path: #ComponentScan({...., "com.microsoft.applicationinsights.web.spring"})
Results:
I see no logs about looking up the configuration file, even if I make the syntax error in in or remove it completely
in debug I see that RequestNameHandlerInterceptorAdapter is instantiated via com.microsoft.applicationinsights.web.spring.internal.InterceptorRegistry, and during calls the preHandle method is called, but calls to ThreadContext.getRequestTelemetryContext() returns always null and nothing more happens
It looks like it is something obvious, but no idea what. What part/classes are responsible for loading the configuration file?
I was a little bit confused with documentation. As mentioned by yonisha, the filter does the whole magic. The following configuration class takes care of creating and adding the filter in Spring Boot application.
import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
#Configuration
#ComponentScan("com.microsoft.applicationinsights.web.spring")
public class ApplicationInsightsConfiguration {
#Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(appInsightsWebRequestTrackingFilter());
registration.addUrlPatterns("/*");
registration.setName("webRequestTrackingFilter");
registration.setOrder(1);
return registration;
}
#Bean(name = "appInsightsWebRequestTrackingFilter")
public Filter appInsightsWebRequestTrackingFilter() {
return new WebRequestTrackingFilter();
}
Important: It will work nicely if you set the server.context-path property to some value. If not, AI initialization will fail with error
AI: ERROR 03-04-2017 14:11, 20: WebApp name is not found, unable to register WebApp
In order to keep servlet-context empty, I had to implement wrappers for the filter and 2 other classes to override it, but it was a very dirty fix... Would be great, if the name could be passed as a parameter to the filter, but that is not yet possible (https://github.com/Microsoft/ApplicationInsights-Java/issues/359)
In spring boot , We need to configure WebRequestTrackingFilter by extending WebSecurityConfigurerAdapter and overriding configure(HttpSecurity httpSecurity)
#Bean
public WebRequestTrackingFilter applicationInsightsFilterBean() throws Exception {
WebRequestTrackingFilter webRequestTrackingFilter = new WebRequestTrackingFilter();
return webRequestTrackingFilter;
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
//other stuff...
httpSecurity.addFilterBefore(applicationInsightsFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
you need to have below configuration also ..
applicationinsights-web dependency in place via maven
added ApplicationInsights.xml to main/resources
Here is newer guide for Spring Boot Application Insights integration that worked well for me just now:
https://github.com/AzureCAT-GSI/DevCamp/tree/master/HOL/java/06-appinsights
The idea is basically what Tomasz has above with some minor differences.
package devCamp.WebApp.configurations;
import javax.servlet.Filter;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.microsoft.applicationinsights.TelemetryConfiguration;
import com.microsoft.applicationinsights.web.internal.WebRequestTrackingFilter;
#Configuration
public class AppInsightsConfig {
#Bean
public String telemetryConfig() {
String telemetryKey = System.getenv("APPLICATION_INSIGHTS_IKEY");
if (telemetryKey != null) {
TelemetryConfiguration.getActive().setInstrumentationKey(telemetryKey);
}
return telemetryKey;
}
#Bean
public FilterRegistrationBean aiFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new WebRequestTrackingFilter());
registration.addUrlPatterns("/**");
registration.setOrder(1);
return registration;
}
#Bean(name = "WebRequestTrackingFilter")
public Filter WebRequestTrackingFilter() {
return new WebRequestTrackingFilter();
}
}
The guide at the link above has a full set of instructions and includes client side js and a java log appender example as well. Hope this helps.
The above all method works! However you can try the whole new seamless experience using Application Insights SpringBoot Starter.
https://github.com/Microsoft/ApplicationInsights-Java/blob/master/azure-application-insights-spring-boot-starter/README.md
This is currently in BETA
I am new to Mockito as a concept. Can you please help me understand using Mockito for formhandlers in ATG. Some examples will be appreciated.
There is a good answer (related to ATG) for other similar question: using-mockito-for-writing-atg-test-case. Please review if it includes what you need.
Many of ATG-specific components (and form handlers particularly) are known to be "less testable" (in comparison to components developed using TDD/BDD approach), b/c design of OOTB components (including reference application) doesn't always adhere to the principle of having "Low Coupling and High Cohesion"
But still the generic approach is applicable for writing unit-tests for all ATG components.
Below is a framework we've used for testing ATG FormHandlers with Mockito. Obviously you'll need to put in all the proper bits of the test but this should get you started.
public class AcmeFormHandlerTest {
#Spy #InjectMocks private AcmeFormHandler testObj;
#Mock private Validator<AcmeInterface> acmeValidatorMock;
#Mock private DynamoHttpServletRequest requestMock;
#Mock private DynamoHttpServletResponse responseMock;
private static final String ERROR1_KEY = "error1";
private static final String ERROR1_VALUE = "error1value";
#BeforeMethod(groups = { "unit" })
public void setUp() throws Exception {
testObj = new AcmeFormHandler();
initMocks(this);
}
//Test the happy path scenario
#Test(groups = { "unit" })
public void testWithValidData() throws Exception {
testObj.handleUpdate(requestMock, responseMock);
//Assume your formhandler calls a helper method, then ensure the helper method is called once. You verify the working of your helper method as you would do any Unit test
Mockito.verify(testObj).update(Matchers.refEq(requestMock), Matchers.refEq(responseMock), Mockito.anyString(), (AcmeBean) Mockito.anyObject());
}
//Test a validation exception
#Test(groups = { "unit" })
public void testWithInvalidData() throws Exception {
Map<String, String> validationMessages = new HashMap<String, String>();
validationMessages.put(ERROR1_KEY, ERROR1_VALUE);
when(acmeValidatorMock.validate((AcmeInterface) Mockito.any())).thenReturn(validationMessages);
testObj.handleUpdate(requestMock, responseMock);
assertEquals(1, testObj.getFormExceptions().size());
DropletFormException exception = (DropletFormException) testObj.getFormExceptions().get(0);
Assert.assertEquals(exception.getMessage(), ERROR1_VALUE);
}
//Test a runtime exception
#Test(groups = { "unit" })
public void testWithRunProcessException() throws Exception {
doThrow(new RunProcessException("")).when(testObj).update(Matchers.refEq(requestMock), Matchers.refEq(responseMock), Mockito.anyString(), (AcmeBean) Mockito.anyObject());
testObj.handleAddGiftCardToCart(requestMock, responseMock);
assertEquals(1, testObj.getFormExceptions().size());
DropletFormException exception = (DropletFormException) testObj.getFormExceptions().get(0);
Assert.assertEquals(exception.getMessage(), GENERAL_ERROR_KEY);
}
}
Obviously the above is just a framework that fit in nicely with the way in which we developed our FormHandlers. You can also add validation for redirects and stuff like that if you choose:
Mockito.verify(responseMock, Mockito.times(1)).sendLocalRedirect(SUCCESS_URL, requestMock);
Ultimately the caveats of testing other people's code still applies.
Here's what I do when I unit test a form handler (at least until I manage to release a major update for AtgDust). Note that I don't use wildcard imports, so I'm not sure if this causes any namespace conflicts.
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import org.junit.*;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import atg.servlet.*;
import some.form.handler.FormHandler;
#RunWith(JUnit4.class)
public class FormHandlerTest {
#Mock DynamoHttpServletRequest request;
#Mock DynamoHttpServletResponse response;
FormHandler handler;
#Before
public void setup() {
initMocks(this);
handler = new FormHandler();
}
#Test
public void testSubmitHandlerRedirects() {
handler.handleSubmit(request, response);
verify(response).sendLocalRedirect(eq("/success.jsp"), eq(request));
assertThat(handler.getFormError(), is(false));
}
}
The basic idea is to set up custom behavior for mocks/stubs using when() on the mock object method invocation to return some test value or throw an exception, then verify() mock objects were invoked an exact number of times (in the default case, once), and do any assertions on data that's been changed in the form handler. Essentially, you'll want to use when() to emulate any sort of method calls that need to return other mock objects. When do you need to do this? The easiest way to tell is when you get NPEs or other runtime exceptions due to working with nulls, zeros, empty strings, etc.
In an integration test, ideally, you'd be able to use a sort of in-between mock/test servlet that pretends to work like a full application server that performs minimal request/session/global scope management. This is a good use for Arquillian as far as I know, but I haven't gotten around to trying that out yet.