passing program arguments to spring boot - spring-boot

I have a spring boot batch application which use program arguments to get some files and manipulate it. the app works fine but i have problems when running the junit tests.
Here is my code :
#Component
public class ApplicationArguments implements InitializingBean {
#Autowired private org.springframework.boot.ApplicationArguments appArgs;
private String filePath;
#Override
public void afterPropertiesSet() throws Exception {
filePath = appArgs.getSourceArgs()[0];
}
}
this bean is used by another one to build the full path :
#Component
public class InitPaths implements InitializingBean {
#Autowired private ApplicationArguments myAppArgs;
private String fullPath;
#Override
public void afterPropertiesSet() throws Exception {
fullPath = myAppArgs.getFilePath(); //this will be null when launching tests
fullPath.toString();//this will throw a NullPointerException if we run the test
}
}
the application works fine using this command :
java -jar myApp.jar fileName.txt
is there any solution to pass the same argument to the junit test ?
I tried to use mocks but i had the same issue :
#RunWith(SpringRunner.class)
#SpringBootTest
public class BatchTest {
#MockBean
ApplicationArguments applicationArguments;
#Autowired
#InjectMocks
InitPaths initPaths;
#Before
public void before() {
when(applicationArguments.getFilePath()).thenReturn("myCustomFile.dat");
}
#Test
public void contextLoad() {
}
}
Here is the error :
Invocation of init method failed; nested exception is java.lang.NullPointerException

The problem is because method afterPropertiesSet() in InitPaths have been running a earlier them before in test. Thats mean your mocked ApplicationArguments do not have any mocked behavior. From my perspective you might create new mocked ApplicationArguments with predefined behavior
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(ApplicationArgumentsTestConfig.class)
public class BatchTest {
#Autowired
InitPaths initPaths;
#Test
public void contextLoad() {
}
public static class ApplicationArgumentsTestConfig {
#Bean
#Primary
public ApplicationArguments mockArg() {
ApplicationArguments mocked = Mockito.mock(ApplicationArguments.class);
Mockito.when(mocked.getFilePath()).thenReturn("test-mock-path");
return mocked;
}
}
}
I just checked work for me.

Related

Not able to mock jdbcTemplate(datasource) in springboot test class using testNG

I am getting a null pointer exception on jdbcTemplate object in this MyMain.java class while mocking the jdbcTemplate.update() method. I have used #Mock for this in the MyMainTest.java class. Here are my code snippets:
MyMain.java
#Component
public class MyMain {
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("myDatasource")
Datasource myDatasource;
#PostConstruct
public void initialize() {
jdbcTemplate = new JdbcTemplate(myDatasource);
}
public void saveData() {
jdbcTemplate.update("query", "parameter passing here"); // In this line only I am getting Nullpointer exception on jdbcTemplate.
}
}
MyMainTest.java
public class MyMainTest {
#Mock
JdbcTemplate jdbcTemplate;
#Test
public void saveDataTest() {
when(jdbcTemplate.update(Mockito.any(), Mockito.any()).thenReturn(1);
new MyMain().saveData();
}
}
I have tried several alternative ways and I am still getting this issue.
Note: I am using TestNG for running the test classes. It's a Spring Boot project.

Verifying pointcuts being called in tests

I have a dummy project where I try figure out how to test pointcuts being triggered.
My project consists of 1 aspect bean which just prints after a foo method is called
#Component
#Aspect
public class SystemArchitecture {
#After("execution(* foo(..))")
public void after() {
System.out.println("#After");
}
}
And a FooServiceImpl with implemented foo method
#Service
public class FooServiceImpl implements FooService{
#Override
public FooDto foo(String msg) {
return new FooDto(msg);
}
}
The code works and and I can see "#After" being printed to console, but I can't check programatically if after pointcut was called using the test below.
#SpringBootTest
public class AspectTest {
#Autowired
private FooService fooService;
#Test
void shouldPass() {
fooService.foo("hello");
}
}
I've also tried using non-bean proxy as was adviced in https://stackoverflow.com/a/56312984/18224588, but this time I'm getting an obvious error cannot extend concrete aspect because my spy proxy is no longer viewed as an aspect:
public class AspectNoContextTest {
#Test
void shouldPass() {
FooService fooService = Mockito.mock(FooService.class);
SystemArchitecture systemArchitecture = Mockito.spy(new SystemArchitecture());
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(fooService);
aspectJProxyFactory.addAspect(systemArchitecture);
DefaultAopProxyFactory proxyFactory = new DefaultAopProxyFactory();
AopProxy aopProxy = proxyFactory.createAopProxy(aspectJProxyFactory);
FooService proxy = (FooService) aopProxy.getProxy();
proxy.foo("foo");
verify(systemArchitecture, times(1)).after();
}
}
Ok, after some digging, I found that it's possible to accomplish this by making an aspect a #SpyBean. Also AopUtils can be used for performing additional checks
#SpringBootTest
public class AspectTest {
#Autowired
private FooService fooService;
#SpyBean
private SystemArchitecture systemArchitecture;
#Test
void shouldPass() {
assertTrue(AopUtils.isAopProxy(fooService));
assertTrue(AopUtils.isCglibProxy(fooService));
fooService.foo("foo");
verify(systemArchitecture, times(1)).after();
}
}

Autowired service is null in embedded groovy script

I have a spring boot application that I wish to script with groovy.
I wish to autowire spring boot services to the script but when I compile and run the script autowired accountService turns out to be null.
I see "setApplicationContext called with org.springframework.context.annotation.AnnotationConfigApplicationContext#34b56772" in the log so setApplicationContext does get called.
What am I missing. Thanks.
PS. I cleaned the code a little to better illustrate the problem.
More #1:
Purpose of the application is to extend some base functionality that is written in java , using groovy scripts.
hello.groovy is loaded from resources folder and extends CustomScript which has more functionality that script will override in production.
CustomScript is now ApplicationContextAware but autowired AccountService in groovy script is still null.
I need to make Spring know about the groovy script that extended CustomScript that I instantiated using "newInstance".
Making CustomScript ApplicationContextAware by implementing ApplicationContextAware and/or making groovy script itself ApplicationContextAware by implementing ApplicationContextAware didn't help so far.
The accountService in "#Autowired AccountService accountService" line in the groovy script always returns null.
AccountService.java
#Service
public class AccountService
{
public void hello()
{
System.out.println( "hello from account" );
}
}
CustomScriptInterface.java
public interface CustomScriptInterface
{
public void main();
}
CustomScript.java
#Component
public class CustomScript implements CustomScriptInterface, Serializable, ApplicationContextAware, BeanNameAware
{
private ApplicationContext applicationContext;
private String name;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
System.out.println( "*** setApplicationContext called with " + applicationContext );
this.applicationContext = applicationContext;
}
public ApplicationContext getApplicationContext()
{
return applicationContext;
}
#Override
public void setBeanName(String name)
{
this.name = name;
}
public void main()
{
}
}
CustomScriptImportCustomizer.java
public class CustomScriptImportCustomizer extends ImportCustomizer
{
public CustomScriptImportCustomizer()
{
addStarImports("gro2vy");
addStarImports("org.springframework.beans.factory.annotation");
addImports("org.springframework.stereotype.Component");
}
}
Gro2vyApplication.java
#SpringBootApplication
public class Gro2vyApplication
{
#Autowired private ApplicationContext ctx;
public CustomScript compile(String groovy) throws Throwable
{
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
CustomScriptImportCustomizer importCustomizer = new CustomScriptImportCustomizer();
compilerConfiguration.addCompilationCustomizers( importCustomizer );
ClassLoader classLoader = this.getClass().getClassLoader();
GroovyClassLoader groovyClassLoader = new GroovyClassLoader( classLoader, compilerConfiguration );
Class<CustomScriptInterface> clazz = groovyClassLoader.parseClass( groovy );
CustomScript script = (CustomScript) clazz.newInstance();
return script;
}
public void init() throws Throwable
{
File resource = new ClassPathResource( "hello.groovy" ).getFile();
String groovy = new String( Files.readAllBytes(resource.toPath() ) );
CustomScript script = compile( groovy );
postProcess( script );
script.main();
}
public void postProcess(Object object) throws BeansException, IllegalArgumentException, IllegalAccessException
{
Field[] fields = object.getClass().getDeclaredFields();
for(int i = 0; i < fields.length; i++)
{
Annotation[] annotations = fields[i].getDeclaredAnnotations();
if ( annotations.length > 0 )
{
for(Annotation annotation : annotations)
{
if ( annotation instanceof Autowired )
{
Autowired a = (Autowired) annotation;
Class claz = fields[i].getType();
fields[i].set( object, applicationContext.getBean( claz ) );
}
}
}
}
}
public static void main(String[] args) throws Throwable
{
SpringApplication.run(Gro2vyApplication.class, args);
new Gro2vyApplication().init();
}
}
hello.groovy
class Runner extends CustomScript
{
// has to be defined public
#Autowired public AccountService accountService
#Override
public void main() throws Throwable
{
accountService.hello()
}
}
You are creating a new instance for Runner class using reflection and not leveraging Spring's Dependency Injection. Either you need to instantiate AccountService as well while you instantiate Runner, because that way you are not using Spring container capabilities. Or, you need to use ApplicationContextAware on your Runner class.
I solved my problem as follows:
I instantiate my groovy script which has an autowired Spring service field.
Before I call the groovy script's main method , I process the autowired annotations in it and "autowire" them myself.
If anyone has a more elegant solution , I'd like to hear. Thanks.
There is an abstract class groovy.lang.Script, which lets you CustomScript extends groovy.lang.Script, and you could put your #Autowire service to that Binding property and run with the exec() method

Spring batch + repository Testing

This is my reader that work in job and step i'u using a repository to get users(public interface UserRepository extends JpaRepository<User,Long>):
#Slf4j
public class Reader implements ItemReader<User> {
#Autowired
UserRepository userRepository;
private Iterator<User>userIterator;
#BeforeStep
public void before(StepExecution execution){
userIterator=userRepository.findAll().iterator();
}
#Override
public User read() {
if (userIterator != null && userIterator.hasNext()) {
User user=userIterator.next();
log.info("User-->"+user.toString());
return user;
} else {
return null;
}
}
}
This is my test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringBatchApplication.class, BatchTestConfiguration.class})
public class SpringBatchApplicationTest {
#Autowired
private JobLauncherTestUtils testUtils;
#Autowired
private BatchConfig config;
#Test
public void testEntireJob() throws Exception {
final JobExecution result = testUtils.getJobLauncher().run(config.processJob(), testUtils.getUniqueJobParameters());
Assert.assertNotNull(result);
Assert.assertEquals(BatchStatus.COMPLETED, result.getStatus());
}
#Test
public void testSpecificStep() {
Assert.assertEquals(BatchStatus.COMPLETED, testUtils.launchStep("orderStep1").getStatus());
}
}
When i`m running my test i got a :
Caused by: java.lang.IllegalStateException: Cannot determine embedded database for tests. If you want an embedded database please put a supported one on the classpath.
What do i need to add to make determine of my database. Do i need to place application properties somewhere or something else?
There is how my test situate in project enter image description here

No primary or default constructor found for Pageable in Pact Contract Provider test

I set up following pact contract provider test
#RunWith(SpringRestPactRunner.class)
#Provider("structures")
#PactFolder("pacts")
#VerificationReports({"console", "markdown"})
#SpringBootTest
public class ContractTest {
#MockBean
private MyServiceImpl myServiceImpl;
#Autowired
private MyController myController;
#Configuration
public static class TestConfiguration {
#Bean
public MyController myController() {
return new MyController();
}
}
#TestTarget
public final MockMvcTarget target = new MockMvcTarget();
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
target.setControllers(myController);
}
#State("My state")
public void setupDocumentWithStructures() {
Mockito.when(myService.getStructuresByDocumentId(
ArgumentMatchers.eq("1"),
ArgumentMatchers.any()
)).thenReturn(new PageImpl<>(Arrays.asList(
Structure.of("first"),
Structure.of("second")
)));
}
}
Running the test results in:
java.lang.AssertionError:
0 - Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface org.springframework.data.domain.Pageable
java.lang.IllegalStateException: No primary or default constructor found for interface org.springframework.data.domain.Pageable
The method getStructuresByDocumentId expects a Pageable object as its second argument. Changing the annotation #SpringBootTest to
#WebMvcTest(MyController.class)
#EnableSpringDataWebSupport
Doesn't solve the problem. Any ideas, how to solve this issue?
you used "myService" in your setupDocumentWithStructures whereas your #MockBean is myServiceImpl.......I think you meant to use myServiceImpl in setupDocumentWithStructures
That's how it can work
#Before
public void setupOrInit() {
this.mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setControllerAdvice(new ErrorRequestInterceptor(tracer))
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build();
}
I was having the same problem and fixed setting a new mockMvc like this
#Before
public void before() {
MockitoAnnotations.initMocks(this);
target.setMockMvc(MockMvcBuilders.standaloneSetup(myController)
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build());
}
I am not using #SpringBootTest as you are, but I think in this case it does not matter. Below is my entire (redacted) code.
#RunWith(SpringRestPactRunner.class)
#Provider("my-provider")
#PactBroker(url = "https://pact-broker.my-compnay.com")
public class MyControllerProviderContractTest {
#TestTarget
public final MockMvcTarget target = new MockMvcTarget();
#Mock
private MyService myService;
#InjectMocks
private MyController myController = new MyController();
#Before
public void before() {
MockitoAnnotations.initMocks(this);
target.setMockMvc(MockMvcBuilders.standaloneSetup(myController)
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build());
}
#State("my state")
public void stateForMyMethod() {
//my mocks
}
}
I hope this helps, I spend a few hours trying to solve this.
Cheers

Resources