I have a private String variable filePath that will be set in the SpringBoot's execute(..) method and then the value will be used in another method that will be called from inside this execute(..).
#Component("filebatchjobtask")
public class FileBatchJobTask extends BaseFileBatchJobTask implements Tasklet {
private String filePath; // PRIVATE VARIABLE THAT WILL BE USED IN A CALL
private static final CalLogger LOGGER = CalLoggerFactory.getLogger(FileBatchJobTask.class);
#Override
public RepeatStatus execute(final StepContribution stepContribution, final ChunkContext chunkContext) throws Exception {
// INITIALIZE PRIVATE VARIABLE HERE
filePath = chunkContext.getStepContext().getJobParameters().get(Constants.FILEPATH).toString();
processeFile(); // METHOD CALL WHERE FILEPATH INITIALIZED ABOVE WILL BE USED
return RepeatStatus.FINISHED;
}
#Override
protected void processeFile() throws IOException {
LOGGER.warn("FileBatchJobTask:processeFile():: Directory to process files: " + filePath);
File[] filelist = geteFiles(filePath); // THIS IS THE CALL I WANT TO MOCK
if (filelist == null || filelist.length < 1) {
LOGGER.warn("FileBatchJobTask: No eFiles available to process");
return;
}
LOGGER.warn("Total number of files to process: " + filelist.length);
}
It's corresponding test below:
//#RunWith(PowerMockRunner.class)
#RunWith(MockitoJUnitRunner.class)
public class FileBatchJobTaskTest extends BaseFileBatchJobTaskTest {
#InjectMocks
FileBatchJobTask fileBatchJobTask;
#Override
BaseFileBatchJobTask createFileBatchJobTask() {
return fileBatchJobTask;
}
#Test
public void processeFile() {
BaseFileBatchJobTask batchJobTask = Mockito.spy(createFileBatchJobTask());
// THIS resourceDir is the I want to use instead of filePath variable in tests here and pick file from this test resource path
Path resourceDir = Paths.get("src", "test", "resources", "data", "validation");
resourcePath = resourceDir.toFile().getAbsolutePath();
File fileDir = new File(resourcePath);
File[] files = fileDir.listFiles(new FileFilter() {
#Override
public boolean accept(final File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(".xml") && pathname.isFile();
}
});
doReturn(files).when(batchJobTask).geteFiles(anyString()); // THIS IS THE CALL I AM TRYING TO MOCK
try {
fileBatchJobTask.processeFile();
Assert.assertTrue(true);
} catch (...) {
}
}
This is the base class
class BaseFileBatchJobTask {
protected File[] geteFiles(final String eFileDirPath) {
File fileDir = new File(eFileDirPath); // NPE as eFileDirPath is null
File[] files = fileDir.listFiles(new FileFilter() {
#Override
public boolean accept(final File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(".xml") && pathname.isFile();
}
});
return files;
}
}
ERROR: I am getting NPE as when the test is run, getEFiles() is executed and filePath is null. Since I am mocking, it shouldn't go inside the actual implementation of the method. However, seems it's not being mocked as expected, so need help in figuring out the issue.
Also looked up a lot of SO posts but couldn't figure out the issue so please don't mark as duplicate if you don't know the answer :)
You need to call processeFile() on the spied version of your jobTask, not on the original one. Think about a spy being a wrapper around the spied object, that intercepts the mocked calls.
For short, just use batchJobTask inside the try-catch block like this:
try {
batchJobTask.processeFile();
Assert.assertTrue(true);
} catch (...) {
}
Related
How can I test the following service method with Junit test? It's a very simple rebuild of my code and only an example.
I want to test in a JUnit test, what's happening, if the file string is empty or null.
Unfortunately I'm new to testing with JUnit. I read already some examples for rest controller and services and repos to me mocked and the methods for it, but here I have no idea how it could work. May someone can help?
public class MyService {
private String fileName = "src/main/resources";
// or
// private String fileName = ${modulename.config.filename};
// #Autowired
// private RepoService RepoService;
#EventListener
public void init(ContextRefreshedEvent event) {
try {
myMethod();
} catch (Exception e) {
// LOGGING
};
}
public void myMethod() {
if(fileName != null && !fileName.isEmpty()) {
// doSomething with file and IOException
// save in repo
} else {
// LOGGING
}
}
}
public void myMethod() {
if(fileName != null && !fileName.isEmpty()) {
// doSomething with IOException
} else {
// LOGGING
}
}
}
The test should look like the following. I tried to set the value somehow in the test, but it does not make sense and it's not set then.
#SpringBootTest
class DemoApplicationTests {
#Test
public void fileNameTest() {
// GIVEN
//Mockito.when()
// WHEN
// THEN
}
}
To change the value of a private variable Use Field modification using Reflection API.
Here's a nice explanation on how to do this.
I am reading a flat file using spring batch FlatFileItemReader.
I have a requestId field which i need to populate with a unique value for all records read from the flat file.
eg: When i read file1. I want to set the requestId to 1 for all Item objects created at requestId field. For file2, i need to set requestId to 2.
my requestId is uniquely generated by a separate class.
How can I achieve this using spring batch?
there are some possible solutions
use an ResourceAware Item
public class MyItem implements ResourceAware {
private Resource resource;
public String getId() {
return createIdFromResource(resource);
}
private String createIdFromResource(final Resource resource) {
// create your ID here
return resource.getFilename();
}
#Override
public void setResource(final Resource resource) {
this.resource = resource;
}
}
use an Listener (here with interfaces, less verbose use of annotations is possible too)
public class TestListener implements StepExecutionListener, ItemReadListener<String> {
private StepExecution stepExecution;
private static final String CURRENT_ID = "currentId";
#Override
public void beforeStep(final StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(final StepExecution stepExecution) {
return null;
}
#Override
public void beforeRead() {
}
#Override
public void afterRead(final String item) {
String currentId = null;
if (stepExecution.getExecutionContext().containsKey(CURRENT_ID)) {
currentId = stepExecution.getExecutionContext().getString(CURRENT_ID);
} else {
String fileName = stepExecution.getExecutionContext().getString("fileName");
// ... create ID from FileName
currentId = fileName + "foo";
stepExecution.getExecutionContext().put(CURRENT_ID, currentId);
}
}
#Override
public void onReadError(final Exception ex) {
}
}
in the above example the current fileName is avavailable in the stepExecutionContext, it might be you have to pull it from jobParameters and extract the filename
String paramValue = stepExecution.getJobExecution().getJobParameters().getString("paramName");
// extract fileName from paramValue
In a batch service, I read multiple XML files using a MultiResourceItemReader, which delegate to a StaxEventItemReader.
If an error is raised reading a file (a parsing exception for example), I would like to specify to Spring to start reading the next matching file. Using #OnReadError annotation and/or a SkipPolicy for example.
Currently, when a reading exception is raised, the batch stops.
Does anyone have an idea how to do it ?
EDIT: I see MultiResourceItemReader has a method readNextItem(), but it's private -_-
I'm not using SB for a while, but looking MultiResourceItemReader code I suppose you can write your own ResourceAwareItemReaderItemStream wrapper where you check for a flag setted to move to next file or to perform a standard read using a delegate.
This flag can be stored into execution-context or into your wrapper and should be cleared after a move next.
class MoveNextReader<T> implements ResourceAwareItemReaderItemStream<T> {
private ResourceAwareItemReaderItemStream delegate;
private boolean skipThisFile = false;
public void setSkipThisFile(boolean value) {
skipThisFile = value;
}
public void setResource(Resource resource) {
skipThisFile = false;
delegate.setResource(resource);
}
public T read() {
if(skipThisFile) {
skipThisFile = false;
// This force MultiResourceItemReader to move to next resource
return null;
}
return delegate.read();
}
}
Use this class as delegate for MultiResourceItemReader and in #OnReadErrorinject MoveNextReader and set MoveNextReader.skipThisFile.
I can't test code from myself but I hope this can be a good starting point.
Here are my final classes to read multiple XML files and jump to the next file when a read error occurs on one (thanks to Luca's idea).
My custom ItemReader, extended from MultiResourceItemReader :
public class MyItemReader extends MultiResourceItemReader<InputElement> {
private SkippableResourceItemReader<InputElement> reader;
public MyItemReader() throws IOException {
super();
// Resources
PathMatchingResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
this.setResources( resourceResolver.getResources( "classpath:input/inputFile*.xml" ) );
// Delegate reader
reader = new SkippableResourceItemReader<InputElement>();
StaxEventItemReader<InputElement> delegateReader = new StaxEventItemReader<InputElement>();
delegateReader.setFragmentRootElementName("inputElement");
Jaxb2Marshaller unmarshaller = new Jaxb2Marshaller();
unmarshaller.setClassesToBeBound( InputElement.class );
delegateReader.setUnmarshaller( unmarshaller );
reader.setDelegate( delegateReader );
this.setDelegate( reader );
}
[...]
#OnReadError
public void onReadError( Exception exception ){
reader.setSkipResource( true );
}
}
And the ItemReader-in-the-middle used to skip the current resource :
public class SkippableResourceItemReader<T> implements ResourceAwareItemReaderItemStream<T> {
private ResourceAwareItemReaderItemStream<T> delegate;
private boolean skipResource = false;
#Override
public void close() throws ItemStreamException {
delegate.close();
}
#Override
public T read() throws UnexpectedInputException, ParseException, NonTransientResourceException, Exception {
if( skipResource ){
skipResource = false;
return null;
}
return delegate.read();
}
#Override
public void setResource( Resource resource ) {
skipResource = false;
delegate.setResource( resource );
}
#Override
public void open( ExecutionContext executionContext ) throws ItemStreamException {
delegate.open( executionContext );
}
#Override
public void update( ExecutionContext executionContext ) throws ItemStreamException {
delegate.update( executionContext );
}
public void setDelegate(ResourceAwareItemReaderItemStream<T> delegate) {
this.delegate = delegate;
}
public void setSkipResource( boolean skipResource ) {
this.skipResource = skipResource;
}
}
SO! I've reached an impasse regarding code-design. Here's the scenario.
I am required to either copy, move or delete files. OK, sure, no problem. I can easily write it like this.
public class SimpleFileManager {
public void copy(String sourcePath, String targetPath) { ... }
public void move(String sourcePath, String targetPath) { ... }
public void delete(String filePath) { ... }
}
and call it from a client code in a manner like this, for example:
public static void main(String[] args) {
...
fileManager.copy(x, y);
...
}
However, a request arises that some particular POJOs which have the FileManager reference perform specific operations, depending on some configuration. The actual specification of which POJO instance should do what is contained in config.
Here's an example of what I mean:
public static void main(String[] args) {
...
InvokerPojo invokerPojo1 = new InvokerPojo(invokerPojo1Config, fileManager); // this config tells it to copy files only
InvokerPojo invokerPojo2 = new InvokerPojo(invokerPojo2Config, fileManager); // this config tells it to move files only
InvokerPojo invokerPojo3 = new InvokerPojo(invokerPojo3Config, fileManager); // this config tells it to delete files only
}
So, FileManager provides the functionality to do actual operations, while InvokerPojo simply invokes and delegates those methods based on config.
However, I do not want to be coupled to FileManager, because, for example, I may find some library that provides the same functionality but is much better than mine.
So, I was thinking something like this:
public interface FileManagerDelegator {
void copy(String sourcePath, String targetPath);
void move(String sourcePath, String targetPath);
void delete(String filePath);
}
public class MyFileManagerDelegator implements FileManagerDelegator {
private SimpleFileManager simpleFileManager;
void copy(String sourcePath, String targetPath) { // delegate to simpleFileManager }
void move(String sourcePath, String targetPath) { // delegate to simpleFileManager }
void delete(String filePath) { // delegate to simpleFileManager }
}
public class ComplexFileManagerDelegator implements FileManagerDelegator {
private ComplexFileManager complexFileManager;
void copy(String sourcePath, String targetPath) { // delegate to complexFileManager }
void move(String sourcePath, String targetPath) { // delegate to complexFileManager }
void delete(String filePath) { // delegate to complexFileManager }
}
public interface Command {
void execute();
}
public class CopyCommand() {
private FileManagerDelegator delegator;
String sourcePath;
String targetPath;
void execute() {
delegator.copy(sourcePath, targetPath);
}
}
public class MoveCommand() {
private FileManagerDelegator delegator;
String sourcePath;
String targetPath;
void execute() {
delegator.move(sourcePath, targetPath);
}
}
public class DeleteCommand() {
private FileManagerDelegator delegator;
String filePath;
void execute() {
delegator.delete(filePath);
}
}
public static void main(String[] args) {
...
Command c = getCommand(context);
c.execute();
...
}
Now, the problem is in actually creating that particular command, because I do not want to know which command is being created. All I know is there is info in context that creates it.
I was thinking about having a factory that would create the appropriate Command from context.
The main issue arises in number of parameters for command.
If the only operations that existed were copy and move, then it would have been easy
public interface Command {
void execute(String a, String b);
}
However, since there is a delete operation which takes only one, then I'd either have to have to ignore the other argument in the call or add another method to interface, and I consider both to be bad.
I also don't want to send a variable number of arguments like this:
public interface Command {
void execute(String ... args);
}
because it's just a bit prettier version of this beforemention bad design.
So, would a factory based on context be a bit more cleaner, in a way that my client code doesn't know which operation is being called, and on which receiver:
Example of 2 contexts:
Context copyContext = new Context();
copyContext.set("OPERATION", "COPY");
copyContext.set("sourcePath", sourcePath);
copyContext.set("targetPath", targetPath);
Context deleteContext = new Context();
deleteContext.set("OPERATION", "DELETE");
deleteContext.set("filePath", filePath);
And then, in the factory, I could do something like this:
Command getCommand(FileManagerDelegator delegator, Context context) {
String operation = context.get("OPERATION");
if (operation.equals("COPY")) {
String sourcePath = context.get("sourcePath");
String targetPath = context.get("targetPath");
return new CopyCommand(sourcePath, targetPath, delegator);
} else if (operation.equals("DELETE")) {
String filePath = context.get("filePath");
return new DeleteCommand(filePath, delegator);
} else {
...
}
}
Is there a cleaner, more configurable way to create parametrized command objects on the fly (dynamically configure them, from context) that operate with different (number of) arguments?
I have a problem with JUnit test for my method
#Transactional
#Override
public void deleteOffer(Offer offer) {
List<String> offerPictures = this.getOfferPictures(offer);
if (offerPictures != null) {
System.out.println(offerPictures.size());
for (String stringName : offerPictures) {
this.deleteSinglePhoto(new File(hardDiscAddress + stringName));
this.deleteSinglePhoto(new File(hardDiscAddress + "sm_" + stringName));
}
}
offerDAO.delete(offer.getId());
}
I already have test for empty offerPcitures list, but now I need to write one for NOT empty list. Problem is I don't know how to mock getOfferPictures method to return not empty string list
#Override
public List<String> getOfferPictures(Offer offer) {
File dir = new File(hardDiscAddress);
List<String> resultantlist = new ArrayList<String>();
if (dir.isDirectory()) {
for (final File f : dir.listFiles()) {
if (f.getName().startsWith(offer.getPhotography())) {
resultantlist.add(f.getName());
}
}
}
return resultantlist;
}
And this is the test for empty list
#Test
public void testDeleteOffer() {
// given
testOfferServiceImpl = new OfferServiceImpl();
testOfferServiceImpl.hardDiscAddress = "C:/";
testOfferServiceImpl.offerDAO = offerDAOMock;
when(offerMock.getId()).thenReturn(1);
when(offerMock.getPhotography()).thenReturn("stringForTest");
doNothing().when(offerDAOMock).delete(1);
// when
testOfferServiceImpl.deleteOffer(offerMock);
// then
Mockito.verify(offerDAOMock, times(1)).delete(1);
}
You could use Mockito.spy to partially mock the instance you're testing, and return a non empty list:
#Test
public void testNotEmptyOffer() {
// Given
testOfferServiceImpl = new OfferServiceImpl();
testOfferServiceImpl.hardDiscAddress = "C:/";
testOfferServiceImpl.offerDAO = offerDAOMock;
// Spy (partially mock) the object
testOfferServiceImpl = Mockito.spy(testOfferServiceImpl);
doReturn(Arrays.asList("one", "two", "three")).
when(testOfferServiceImpl).getOfferPictures(offerMock)
// Test your logic here
}
Use something like Mockito. You can do:
Candidate candidate = mock(Candidate.class);
when(candidate.getFirstName()).thenReturn("Bob");
this is an example from project, works well. You will also have to annotate your test class with:
#RunWith(MockitoJUnitRunner.class)
you can then make the method return whatever you like. Hope that helps :)