Testing a method that reads and processes data from a file [duplicate] - java

This question already has answers here:
Mocking Files in Java - Mock Contents - Mockito
(3 answers)
Closed 6 years ago.
I have the following code:
public class FolderServiceImpl implements FolderService {
private static final Logger L = LoggerFactory.getLogger(FolderServiceImpl.class);
public int getStatus(String folderPath) {
int status = 0;
File folderStatusFile = new File(folderPath, ".folderstatus");
if (folderStatusFile.exists()) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(folderStatusFile));
String line = br.readLine();
status = Integer.parseInt(line);
} catch (Exception e) {
L.error("can't read file " + folderStatusFile.getAbsolutePath(), e);
status = 4;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
L.warn("could not close reader ", e);
}
}
}
} else {
status = 3;
}
return status;
}
}
I want to test this method without creating actual files for every case. I should be using Java 1.7, JUnit 4, Mockito and/or PowerMockito.
Any ideas on how to do that?
I am talking about mocking either the data source or simply changeing the input for the method.
My test looks something like this:
`#Rule
public TemporaryFolder folder = new TemporaryFolder();
private FolderServiceImpl serviceToTest = new FolderServiceImpl();
private String folderPath;
#Before
public void setUp() {
folderPath = folder.getRoot().getAbsolutePath();
try {
folder.newFile(".folderstatus");
} catch (IOException e) {
e.printStackTrace();
}
}
#Test
public void shouldReturnFolderStatus3WhenFolderStatusIsNotFound() {
// given
deleteFolderStatusFile();
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(3, status);
}
#Test
public void shouldReturnFolderStatus4WhenTheStatusIsUnreadable() {
// given
writeStatusToTestFile("Test");
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(4, status);
}
#Test
public void shouldReturnFolderStatusInTheFile() {
// given
writeStatusToTestFile("1");
// actual
int status = serviceToTest.getFolderStatus(folderPath);
// expected
assertEquals(1, status);
}
private void writeStatusToTestFile(String status) {
Path file = Paths.get(folder.getRoot().getAbsolutePath(), ".folderstatus");
try {
Files.write(file, status.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private void deleteFolderStatusFile() {
Path file = Paths.get(folder.getRoot().getAbsolutePath(), ".folderstatus");
try {
Files.delete(file);
} catch (IOException e) {
e.printStackTrace();
}
}`

Although the answer of #BenHeid may work I'd suggest to change to different approach.
IMHO when ever I use PowerMock(-ito) it is a surrender to bad design.
Also the PowerMock solution will confuse test coverage tools since it changes the Applications byte code after it has been instrumented for coverage measurement.
So the approach I'd prefer is to stick to Clean Code and OOP rules.
One of them is separation of concerns.
In your case the method creates some infrastructure classes (dependencies) to work with, namely FileReader and BufferedReader.
But the instantiation of (direct) dependencies is not a responsibility of a class containing business logic.
Therefore I'd suggest to refactor that code out into a separate class:
class ReaderFactory {
public BufferedReader createFor(File file) throws FileNotFoundException {
return new BufferedReader(new FileReader(file));
}
}
Your Class would change to this:
class FolderServiceImpl {
private static final Logger L = LoggerFactory.getLogger(FolderServiceImpl.class);
private final ReaderFactory readerFactory;
FolderServiceImpl(ReaderFactory readerFactory) {
this.readerFactory = readerFactory;
}
public int getStatus(String folderPath) {
int status = 0;
File folderStatusFile = new File(folderPath, ".folderstatus");
// try "with resource" takes care of closing the reader
try (BufferedReader br = readerFactory.createFor(folderStatusFile);) {
String line = br.readLine();
status = Integer.parseInt(line);
} catch (IOException e) {
status = 3;
} catch (Exception e) {
L.error("can't read file " + folderStatusFile.getAbsolutePath(), e);
status = 4;
}
return status;
}
}
And your Test would be this:
public class FolderServiceImplTest {
private static final String ANY_FILE_NAME = "";
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Rule
public ExpectedException thrown = ExpectedException.none();
#Mock
private ReaderFactory readerFactory;
#InjectMocks
FolderServiceImpl sut;
#Test
public void getStatus_FileNotExisting_returnStatus3() throws Exception {
// arrange
Mockito.doThrow(new FileNotFoundException("UnitTest")).when(readerFactory).createFor(Mockito.any(File.class));
// act
int status = sut.getStatus(ANY_FILE_NAME);
// assert
Assert.assertThat("status",status,CoreMatchers.equalTo(3));
}
#Test
public void getStatus_ValidFile_returnFileContentAsInt() throws Exception {
// arrange
BufferedReader bufferedReader = Mockito.mock(BufferedReader.class);
Mockito.doReturn(bufferedReader).when(readerFactory).createFor(Mockito.any(File.class));
Mockito.doReturn("4711").when(bufferedReader).readLine();
// act
int status = sut.getStatus(ANY_FILE_NAME);
// assert
Assert.assertThat("status",status,CoreMatchers.equalTo(4711));
}
}

You have to use something like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest(tests.class)
public class test {
#Test
public void test() throws Exception {
File fileMock = Mockito.mock(File.class);
PowerMockito.whenNew(File.class).withArguments(Mockito.anyString(), Mockito.anyString()).thenReturn(fileMock);
FolderServiceImpl sut = new FolderServiceImpl sut ();
Mockito.when(fileMock.exists()).thenReturn(true);
sut.getStatus("");
// Your verifications..
}
}
Powermock will mock the File object which is created in the method getStatus of your class. With Mockito.when you can say what is the return value of folderStatusFile.exists() in your code.
EDIT
I have included the following two jars with maven, but you don't need to use maven: https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4/1.4.6 and https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito/1.4.9 and https://mvnrepository.com/artifact/org.mockito/mockito-all/1.10.19

Related

How to access value of an abstract method in a different class?

AbstractInputFile is an abstract class and has the following code below:
public abstract class AbstractInputFile {
static final public int MIN_BILLING_CYCLE = 1;
static final public int MAX_BILLING_CYCLE = 12;
private File file;
public AbstractInputFile() {
}
public abstract List<Request> readFile() throws IOException, BarsException;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
}
CSVInputFileImpl is a subclass of AbstractInputFile, thus inheriting the readFile() method. What it does is it reads the contents of a csv file using a buffered reader, stores it in an arraylist, and returns it.
public List<Request> readFile() throws IOException, BarsException {
List<Request> requests = new ArrayList<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String line = "";
//READ CSV FILE
try {
BufferedReader br = new BufferedReader(new FileReader(getFile()));
int row = 1;
int index = 0;
while ((line = br.readLine()) != null) {
String[] data = line.split(",", 3);
//Validate if billing cycle ranges 1-12
try {
int billingCycle = Integer.parseInt(data[0]);
if(billingCycle <= MAX_BILLING_CYCLE && billingCycle >= MIN_BILLING_CYCLE) {
request.setBillingCycle(billingCycle);
} else {
log.error(BILLING_CYCLE_NOT_ON_RANGE + row);
throw new BarsException(BILLING_CYCLE_NOT_ON_RANGE + row);
}
} catch (BarsException e) {
log.error(INVALID_BILLING_CYCLE + row);
throw new BarsException(INVALID_BILLING_CYCLE + row);
}
//Validate start date
try {
LocalDate startDate = LocalDate.parse(data[1], formatter);
request.setStartDate(startDate);
} catch (DateTimeParseException e) {
log.error(INVALID_START_DATE_FORMAT + row);
throw new BarsException(INVALID_START_DATE_FORMAT + row);
}
//Validate end date
try {
LocalDate endDate = LocalDate.parse(data[2], formatter);
request.setStartDate(endDate);
} catch (DateTimeParseException e) {
log.error(INVALID_END_DATE_FORMAT + row);
throw new BarsException(INVALID_END_DATE_FORMAT + row);
}
row++;
//Add the requests to array list created previously
requests.add(index,new Request(request.getBillingCycle(), request.getStartDate(), request.getEndDate()));
index++;
}
//Validate if file exists
} catch (FileNotFoundException e) {
throw new BarsException(NO_SUPPORTED_FILE);
} catch (IOException e) {
throw new IOException(PATH_DOES_NOT_EXIST);
}
return requests;
}
In another public class FileProcessor, I am instructed to get the array list using List<Request> requests = abstractInputFile.readFile(); but is unable to do so because I am getting the error Non-static method 'readFile()' cannot be referenced from a static context.
public List<Request> execute(File file) throws BarsException {
InputFileFactory fact = InputFileFactory.getInstance();
try {
fact.getInputFile(file);
} catch (BarsException e) {
throw new BarsException(NO_SUPPORTED_FILE);
}
List<Request> requests = AbstractInputFile.readFile();
return requests;
}
My question is: How will I be able to retrieve the arraylist and return it in the execute(file) method? There might be a lot more errors in other areas of the code and logic but I'd appreciate any help and suggestions.
First things first, AbstractInputFile.readFile() can't be called for 2 reasons: readFile() in the AbstractInputFile class is abstract, and it is an instance method.
From what you wrote here, you implement the readFile() method in the subclass, CSVInputFileImpl. Therefore, in this case, your readFile() calls should be based on THIS class, not AbstractInputFile. However, this is still classified as an instance method, meaning you need to first create an instance of CSVInputFileImpl before you can call readFile(). I would correct the code like this (I'm making assumptions that the CSVInputFileImpl class is structured the same as AbstractInputFile, but make sure the file instance var is set):
CSVInputFileImpl csvInputFileImpl = new CSVInputFileImpl();
csvInputFileImpl.setFile(file);
List<Request> requests = csvInputFileImpl.readFile();
return requests;

Spring AOP logging thread method

Is there any way to implement AOP logging to public method of class that implements Runnable and ran by ExecutorService?
Thread class
#Component
#Scope("prototype")
public class FileProcessor implements Runnable {
private final LinkedBlockingQueue<File> filesQueue;
private final GiftCertificateMapper certificateMapper;
private final File errorFolder;
private static final ReentrantLock LOCK = new ReentrantLock();
private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessor.class);
public FileProcessor(LinkedBlockingQueue<File> filesQueue, GiftCertificateMapper certificateMapper,
File errorFolder) {
this.filesQueue = filesQueue;
this.certificateMapper = certificateMapper;
this.errorFolder = errorFolder;
}
#Override
public void run() {
File file = null;
try {
while ((file = filesQueue.poll(100, TimeUnit.MILLISECONDS)) != null) {
processFile(file);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOGGER.warn("File thread was interrupted");
} catch (IOException e) {
LOGGER.error("Error processing file {} \n{}", file.getAbsolutePath(), e);
}
}
public void processFile(File file) throws IOException {
if (file != null) {
try {
ObjectMapper objectMapper = new ObjectMapper();
List<GiftCertificate> certificates = Arrays.asList(objectMapper.readValue(file, GiftCertificate[].class));
certificateMapper.insertList(certificates);
file.delete();
} catch (JsonParseException | UnrecognizedPropertyException | InvalidFormatException | DataIntegrityViolationException e) {
moveFileToErrorFolder(file);
}
}
}
private void moveFileToErrorFolder(File file) throws IOException {
try {
LOCK.lock();
Files.move(Paths.get(file.getAbsolutePath()), getPathForMovingFile(file), StandardCopyOption.ATOMIC_MOVE);
} finally {
LOCK.unlock();
}
}
private Path getPathForMovingFile(File fileForMove) {
File fileList[] = errorFolder.listFiles();
int filesWithSameNameCounter = 0;
if (fileList != null && fileList.length > 0) {
for (File file : fileList) {
if (file.getName().contains(fileForMove.getName())) {
filesWithSameNameCounter++;
}
}
}
return filesWithSameNameCounter > 0 ?
Paths.get(errorFolder.getAbsolutePath(), "(" + filesWithSameNameCounter + ")" + fileForMove.getName()) :
Paths.get(errorFolder.getAbsolutePath(), fileForMove.getName());
}
}
Aspect
#Aspect
#Component
#ConditionalOnProperty(
value = "file-processing.logging.enabled",
havingValue = "true",
matchIfMissing = true)
public class FileProcessingLoggingAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(FileProcessingLoggingAspect.class);
#Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))")
public void processFilePointcut() {
}
#Around("processFilePointcut()")
public Object logFileProcessing(ProceedingJoinPoint joinPoint) throws Throwable {
// File file = (File) joinPoint.getArgs()[0];
// long time = System.currentTimeMillis();
Object object = joinPoint.proceed();
// long resultTime = System.currentTimeMillis() - time;
LOGGER.info("Processing of file took milliseconds");
return object;
}
}
In Spring AOP , internal method calls cannot be intercepted.
In the code shared , even though the method processFile() is public , it gets called from run(). This is a self reference / internal method call , which cannot be intercepted.
Details can be read in the documentation
Due to the proxy-based nature of Spring’s AOP framework, calls within
the target object are, by definition, not intercepted. For JDK
proxies, only public interface method calls on the proxy can be
intercepted
A pointcut expression to intercept all external method calls to a class implementing Runnable would be as follows
#Around("this(java.lang.Runnable) && within(com.epam.esm.processor..*)")
public Object logFileProcessing(ProceedingJoinPoint pjp) throws Throwable {
try {
return pjp.proceed();
} finally {
//log
System.out.println("****Logged");
}
}
Scoping designator within() limits the scope to apply the advice.
The point cut #Pointcut("execution(* com.epam.esm.processor.FileProcessor.processFile(java.io.File))") is valid and would work if an external method call happens to it.
Hope this helps.

Unit testing a constructor method

I'm having trouble getting my head round how to unit test a constructor method.
I need to check an error is being thrown. The constructor is:
#Autowired
public BankDetailsValidator() {
try {
logDebugMessage("BankDetailsValidator() constructor");
loadModulusWeightTable();
loadSortCodeSubstitutionTable();
} catch (final IOException e) {
throw new BankDetailsValidationRuntimeException("An error occured loading the modulus weight table or sort code substitution table", e);
}
}
To test this, I need to have the loadModulusWeightTable or loadSortCodeSubstitutionTable throw and IOException.
private void loadModulusWeightTable() throws IOException {
modulusWeightTable.clear();
logDebugMessage("Attempting to load modulus weight table " + MODULUS_WEIGHT_TABLE);
final InputStream in = new FileInputStream(MODULUS_WEIGHT_TABLE);
br = new BufferedReader(new InputStreamReader(in));
try {
String line;
while ((line = br.readLine()) != null) {
final String[] fields = line.split("\\s+");
modulusWeightTable.add(new ModulusWeightTableEntry(fields));
}
logDebugMessage("Modulus weight table loaded");
}
finally {
br.close();
}
}
I was trying to use Spy to have the buffered file reader return a IOException but couldn't get it working due to it being in the constructor.
public class BankDetailsValidatorTest {
#Spy
private BufferedReader mockBufferReader;
#InjectMocks
private CDLBankDetailsValidator testSubject;
#Test(expected = IOException.class)
public void testIOErrorLogging() throws Exception{
when(mockBufferReader.readLine()).thenThrow(new IOException());
testSubject = new CDLBankDetailsValidator();
}
}
I think that class BankDetailsValidator should be refactored. In such scenarios you should extract code responsible for reading data to separate class and inject it to BankDetailsValidator as constructor parameter. After that you could test that reader separately and of course test BankDetailsValidator with mocked reader.
Here's how you could refactor your code:
#Autowired
public BankDetailsValidator(Collection<ModulusWeightTableEntry> table) {
try {
logDebugMessage("BankDetailsValidator() constructor");
this.modulusWeightTable.addAll(table);
loadModulusWeightTable();
loadSortCodeSubstitutionTable();
} catch (final IOException e) {
throw new BankDetailsValidationRuntimeException("An error occured loading the modulus weight table or sort code substitution table", e);
}
}
which removes the loading from file part to maybe a #Configuration.
#Configuration
class WeightTableConfiguration {
#Bean
ModulusWeightTable loadFromFile(#Value("${mwt.filename}") String filename) {
Collection<ModulusWeightTableEntry> table = new ArrayList<>();
logDebugMessage("Attempting to load modulus weight table " + filename);
try (InputStream in = new FileInputStream(filename);
br = new BufferedReader(new InputStreamReader(in))) {
String line;
while ((line = br.readLine()) != null) {
final String[] fields = line.split("\\s+");
table.add(new ModulusWeightTableEntry(fields));
}
logDebugMessage("Modulus weight table loaded");
}
return table;
}
}
Now you can test your validator separately from the loading code, and create a separate test configuration providing a manually created weight table.
Or you parameterize your constructor with a Supplier<WeightTable> and put the loading code into the get() method of an implementing #Component class.

How to bind messages in javafx without using eclipse.osgi NLS?

I have a messages.properties file that contains all string messages used in my application.
I would like to bind these messages to a java class fields and use directly in other classes.
Can this be achieved without using NLS? By some approach in javafx? Because I do not want to add eclipse dependency in UI classes.
Java provides property file reading capability right from the box. You can do adjustment to suit your actual use-case.
For example:
public final class Messages {
private Messages() {
loadFile();
}
private static final class ThreadSafeSingleton {
private static final Messages INSTANCE = new Messages();
}
public static Messages getInstance() {
return ThreadSafeSingleton.INSTANCE;
}
private final Properties props = new Properties();
private void loadFile() {
InputStream is = null;
try {
is = new FileInputStream("messages.properties");
props.load(is);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public String getMessage(String key) {
if (key == null && key.isEmpty()) return "";
return props.getProperty(key);
}
}
Edit
In order to use these values as if it is a constant, you need to pretty much make everything static:
public final class Messages {
private Messages() {} // Not instantiable
private static final Properties props = loadFile(); // Make sure this static field is at the top
public static final String FOO = getMessage("foo");
public static final String BAR = getMessage("bar");
private static Properties loadFile() {
final Properties p = new Properties();
InputStream is = null;
try {
is = new FileInputStream("messages.properties");
p.load(is);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return p;
}
public static String getMessage(String key) {
if (key == null && key.isEmpty()) return "";
return props.getProperty(key);
}
}
Be warned again, the Properties field must always be the top-most field declared in the class, because the class loader will load the fields top-down for all static fields whose value is computed at runtime (i.e. set by a static method).
Another point, this example does not handles what happens if the file is not file - it simply returns a Properties that has no value.

Mock FileInputStream using Mockito/PowerMockito without having an existing file

I am writing a JUnit for a method that uses FileInputStream and in the constructor only the file name is passed. The file is created as part of a servlet request and this file is not stored any where.
I am trying to Mock FileInputStream using PowerMockito so that it gives me a mocked file object. Unfortunately I get FileNotFoundException which is valid but I am not sure how to test this method then because the file doesn't exist.
Method under test:
public String viewReport() throws Exception {
this.inputStream = new FileInputStream(DOCUSIGN_REPORT_FILE);
try {
boolean returnReport = validateRequest();
if (returnReport) {
intgList = this.generateViewIntegrationReportData(getESignUIConfig());
this.createCSVFile(intgList, new FileWriter(DOCUSIGN_REPORT_FILE));
} else {
failureResponse(msgs, 400);
return null;
}
} catch (Exception e) {
e.printStackTrace();
msgs.add(new Message(ESignatureIntegrationMessageTypeEnum.MESSAGE_TYPE_ERROR,
UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_CODE_500, UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_TEXT_SERVICE_ERROR));
failureResponse(msgs, 500);
return null;
}
return UiIntegrationKeyConstants.REPORT_REPSONSE;
}
JUnit test so far.
#Test
public void testViewReport() throws Exception {
Map<String, Object> actionMap = new HashMap<>();
actionMap.put("application", "ESignatureIntegrationAction");
ActionContext.setContext(new ActionContext(actionMap));
FileInputStream inputStream = Mockito.mock(FileInputStream.class);
PowerMockito.whenNew(FileInputStream.class).withAnyArguments().thenReturn(inputStream);
action = new ESignatureIntegrationAction();
action.viewReport();
}
I get an exception when the code reaches to new FileInputStream(DOCUSIGN_REPORT_FILE);
Thanks for the help.
I would suggest to refactor your code in a way that allows testing without a mocking framework.
It could look somewhat like this:
public class YourClass {
// ...
public String viewReport() {
try {
boolean isValidRequest = validateRequest();
if (isValidRequest) {
IntegrationReportCsvFileHandler fileHandler = new IntegrationReportCsvFileHandler();
IntegrationReportData inputData = fileHandler.readData(new FileInputStream(DOCUSIGN_REPORT_FILE));
IntegrationReportGenerator generator = new IntegrationReportGenerator();
IntegrationReportData outputData = generator.processData(inputData, getESignUIConfig());
fileHandler.writeReport(outputData, new FileWriter(DOCUSIGN_REPORT_FILE));
} else {
failureResponse(msgs, 400);
return UiIntegrationKeyConstants.FAILURE_RESPONSE;
}
} catch (Exception e) {
e.printStackTrace();
msgs.add(new Message(ESignatureIntegrationMessageTypeEnum.MESSAGE_TYPE_ERROR,
UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_CODE_500, UiIntegrationKeyConstants.UI_INTEGRATION_ERROR_TEXT_SERVICE_ERROR));
failureResponse(msgs, 500);
return UiIntegrationKeyConstants.FAILURE_RESPONSE;
}
return UiIntegrationKeyConstants.REPORT_RESPONSE;
}
// ...
}
public class IntegrationReportData {
// your custom data structure
// may as well just be a List<Data>
// may be different for input and output
}
public class IntegrationReportException extends Exception {
// your custom exception
public IntegrationReportException(String message) { super(exception); }
}
public class IntegrationReportGenerator {
public IntegrationReportData processData(IntegrationReportData data, ESignConfig config) throws IntegrationReportException {
// here's your logic that requires testing
}
}
public class IntegrationReportCsvFileHandler {
public IntegrationReportData readData(InputStream input) throws IOException {
// read data from given input stream
}
public void writeData(IntegrationReportData data, OutputStreamWriter outputWriter) throws IOException {
// write data to given output stream
}
}
That way the IntegrationReportGenerator would be easily testable.

Categories