I am working on a Java application that will read a file and then after reading it into memory will do further processing .
The requirement for file reading is that the code should read from 'current working directory'.
I have written a method as follows:
public List<String> processFile(String fileName){
String localPath = FileSystems.getDefault().getPath(".").toAbsolutePath() + fileName;
}
This method converts the file into an ArrayList which it returns.
Then using this arraylist further processing needs to be done.
public boolean workOnFile(){
List<String> records = processFile("abc.txt");
// additional logic
}
I am blocked / stumped on how to Junit the file reading part since the requirement is that the file reading needs to occur from 'working directory' so wherever the user will run the program the input file would be read from working directory.
However in case of Junit my test files would be in '\src\main\resources'
As a result test files would not be read by the 'processFile' method since it looks for files in 'current working directory'
One thought is that I need not Junit the file reading but the entire application does something after the file is read - so do I have some 'testing' provisions where while executing Junit I read file in junit and then have provisions in my class under test to inject my testArrayList ?
#Test
public void doSomeValidation() {
String testFile = "XYZ.txt";
ClassUnderTest fixture = new ClassUnderTest();
List<String> testList = /** read file in Junit from /src/main/resources/ **/
/** inject this testList into ClassUnderTest **/
fixture.setFileContent(testList );
/** then continue testing the actual method that needs to be tested **/
assertNotFalse(fixture.workOnFile());
}
To achieve this I would have to change my actual class that needs to be tested to be able to inject the test file read . Something along these lines :
public class ClassUnderTest(){
public List<String> processFile(String fileName){
String localPath = FileSystems.getDefault().getPath(".").toAbsolutePath() + fileName;
}
/** new method used in junit to inject to **/
public void setFileContent(List<String> input){
this.input = input;
}
/** modify this method first check if injected arraylist not null **/
public boolean workOnFile(){
List<String> records;
if(this.input == null){
/** in actual runs this block will execute **/
this.input = processFile("abc.txt");
}
// additional logic
}
}
Is this the right way ?
I somehow feel I am messing around with code to just make it more testable ?
is this even the right approach ?
A simple solution: change your interfaces to be easy to test.
Meaning:
have one method that puts together a file name "in the local path" (the same way your processFile() method builds that file name
then pass the result of that operation to your processFile() method.
In other words: your code limits that method to always compute the full path itself. Which makes it really hard to control, and thus to test.
Thus: dissect your problem into the smallest pieces that are possible.
Then you only need to test:
that your new method Path getLocalPathFor(String fileName) does what it is supposed to do
and then, that your method processFile(Path absFilePath) does what it needs to do (and now, you can test that method with a path that sits anywhere, not just in the local directory)
Related
I am trying to develop an automatic assignment grading script using JUnit in Java. I am simulating user input by passing in test case inputs (read from a file in the classpath) by changing System.in to a ByteArrayInputStream containing the string data in bytes. I want to be able to run multiple test cases to check all cases and then use the Gradle-built report to mark the assignment submission.
Currently, I have been able to simulate user input to reflect the input provided by the test cases using this link: JUnit: How to simulate System.in testing?. I am also changing System.out to a PrintStream to capture the printed statements from the assignment. My main method spawns a new thread, inside which is the assignment's codes done in, as shown below.
The unit tests perform successfully when I am testing each #Test - annotated case individually. However, when I run all the tests contained in a single Test class, JUnit works successfully in the first #Test -annotated case but fails on the eventual ones, which is somewhat unexpected here. Hopefully the codes below will help to clarify the situation.
My Main method:
public static void main(String ... args) {
Assignment assignment = new Assignment();
new Thread(assignment).start();
}
My test class has some #before and #after annotated methods:
#Before
public void setup() {
systemIn = System.in; // global variable
systemOut = System.out; // global variable
output = new ByteArrayOutputStream(); // global variable
System.setOut(new PrintStream(output));
assignment = new Assignment();
}
#After
public void conclude() {
System.setIn(systemIn);
System.setOut(systemOut);
}
I am reading the corresponding file for each test case input and output, and in the function below, I am setting the test case input.
private void setInput(String data) {
ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes());
System.setIn(input);
}
I am running each test case as follows:
#org.junit.Test // 2,3,4 ....for other cases
public void runTestCase1() {
testCases(1);
}
The IO class the assignment is using is as follows:
public static String readLine() { // w/o try/catch blocks
String read = reader.readLine(); // reader -> bufferedReader(new InputStream(System.in))
return read.trim();
}
Also note that I am using Thread.sleep(50) before invoking the main method so that the input test case is successfully read and loaded in System.in in time for Assignment to execute.
When running all tests together, the first test gives the correct output, i.e. the expected and the actual outputs match. However, for the second case and onward, IO.readline() generates a null pointer exception and hence no output is produced by the assignment. Upon investigation, I discovered that during the second case, System.in is no longer pointing to a ByteArrayInputStream, but rather (the old?) BufferedInputStream. I am confused why this might be happening (probably due to a new thread spawning maybe?) and how do I overcome this problem?
I am reading a code of java source file. I see the following code lines given below
GWT.runAsync(Overview.class, new LoadAsyncCallback() {
public void onSuccess() {
if (ApplicationDetails.class.getName().equals("1")))
{
...............
}
So my Point is
1. why it use ApplicationDetails.class file to access getName(). as
usually we use Java source file.
2. GWT.runAsync(Overview.class, new LoadAsyncCallback() {
what is the mean of this line.
even when I open ApplicationDetails sourse file i did not find any getName() method .
is there any difference to use class file or java sourse file
Every Class in Java has getName() method.
Read about code splitting in GWT.
Usually I have these methods responsible for downloading large file/s to a local directory.
It annoys me because I don't really know how to test that properly?
Should I run a test case that downloads these files to a temp directory using Role in Junit? Or maybe ask it to download files to the same local directory in production?
Importantly, such method takes a long time to download 1GB+ file, so is it bad that a test case would take a long time?
What would be an ideal test case for this?
public File downloadFile(File dir, URL url){
//url contains a 1GB or more
//method takes a long time
return file; // it's located in dir
}
My belief here is that you are approaching the problem in the wrong way altogether.
You want to test "if that method works". But this method is highly dependent on "side effects" which are, in this case, failures which can occur at any of these steps:
connection failure: the given URL cannot be accessed (for whatever reason);
network failure: connection is cut while the transfer was in progress;
file system failure: the specified file cannot be created/opened in write mode; or the write fails while you are downloading contents.
In short: it is impossible to test whether the method "works". And what is more, due to the amount of possible failures mentioned above, it means that your method should at least throw an exception so that the caller of the method can deal with it.
Finally, this is 2016; it is assumed below that you use Java 7 or later, but here is how you can rewrite your method:
public Path downloadFile(final Path dir, final URL url)
throws IOException
{
final String filename = /* decide about the filename here */;
final Path ret = dir.resolve(filename);
try (
final InputStream in = url.openStream();
) {
Files.copy(in, ret);
}
return ret;
}
Now, in your tests, just mock the behavior of that method; make it fail, make it return a valid path, make the URL fail on .openStream()... You can simulate the behavior of that method whichever way you want so that you can test how the callers of this method behave.
But such a method, in itself, is just too dependent on "side effects" that it cannot be tested reliably.
I have a simple test case:
public class FileManagerTest {
String dirPath = “/myDir/”
#Before
public void setUp() {
mFileManager = MyFileManager.getInstance();
}
#Test
private void testPersistFiles() {
System.out.println(“testPersistFiles()…”);
//it deletes old files & persists new files to /myDir/ directory
boolean successful =mFileManager.persistFiles();
Assert.assertTrue(successful);
}
#Test
public void testGetFiles() {
System.out.println(“testGetFiles()…”);
mFileManager.persistFiles();
//I double checked, the persistFiles() works, the files are persisted.
List<File> files = mFileManager.getFilesAtPath(dirPath);
Assert.assertNotNull(files); //Failure here!!!!
}
#Test
public void testGetFilesMap() {
System.out.println(“testGetFilesMap()…”);
mFileManager.persistFiles();
Map<String, File> filesMap = mFileManager.getFilesMapAtPath(dirPath);
Assert.assertNotNull(files);
}
}
The persistFiles() function in FileManager delete all files under /myDir/ then persist files again.
As you see above, I have a System.out.println(…) in each test function. When I run it , I can see all the prints in the following order:
testGetFilesMap()…
testGetFiles()…
testPersistFiles()…
However, test is failed at testGetFiles(). Two things I don't understand:
I don’t understand, it is failed at testGetFiles() why I can still see the print testPersistFiles() which sounds like even it is failed, it doesn't stop running, but continues to run the next test testPersistFiles()? What is happening behind the scene in JUnit test case??
Another thing I don’t understand is why testGetFiles() is failed? I can see log that the persistFiles() has persisted files. Why it got null after that?
I don’t understand, it is failed at testGetFiles() why I can still see the print testPersistFiles() which sounds like even it is failed, i
That is how unit testing works. Each test should be isolated and working using only its set of data. Unit test frameworks run every test so you can see which parts of the system work and which do not, they do not stop on the first failure.
mFileManager.getFilesAtPath(dirPath);
You are not searching the files in the right place
String dirPath = “/myDir/”
Are you sure that this path is ok? with a slash before the directory name?
For each of your tests, JUnit creates a separate instance of that class and runs it. Since you seem to have 3 tests, JUnit will create 3 instances of your class, execute #Before on each of them to initialize state, and then run them.
The order in which they are run is typically the order in which the tests are written but this is not guaranteed.
Now about the print statement - you see that it's the first statement in your test so it will be executed. Then mFileManager.persistFiles(); is executed. For some reason it returns a false and hence the test fails.
As to why it returns false, you can run a local debugger, put a break point at the beginning of that method, single-step and see.
I was doing some work for college and my main runs this:
Spreadsheet sheet = new Spreadsheet(0,0);
SpreadsheetManager manager = new SpreadsheetManager(sheet);
/* Read an Import file, if any */
String filename = System.getProperty("import");
if (filename != null)
sheet.parseInputFile(filename, sheet);
Thing is, when I actually try to import a file it doesn't do what is supposed to and the filename is always null, so it never reaches my parseInputFile.
My teachers made a bunch of code for different programming exercises that do similar things available, and I've also looked at projects my colleagues did in previous years, but every single one does what I am doing above.
I have to run my program like this: java -Dimport=A-002-002-M-ok.import calc.textui.Calc otherwise none of the tests given by the teachers will run.
I'm sorry if this is not a useful question, but I've tried looking everywhere. If anyone could explain how the System.getProperty("import") works and why it isn't working in this case, I would be very grateful.
I suggest you take a look at the documentation of System.getProperty().
Basically it retrieves a value from the system, either already present or set by you.
To avoid retrieving null you can use another method signature that specify a default value:
System.getProperty("import", "file.txt");
To set a System property, you can specify it at launch:
java -Dimport="file.txt" your_application
or set it programatically :
System.setProperty("import", "file.txt");
When you run your program with:
java -Dimport=foo
then the method call
System.getProperty("import")
should return "foo".
Is ist possible that you write a tiny example program to convince yourself? Without any SheetManagers and all stuff, just
class ItWorks {
public static void main(String[] args) {
System.out.println(System.getProperty("import"));
}
}
Call it thus
java -Dimport=indeed ItWorks
and report what happens.
That being said: if you want to pass command line arguments, why don't you use the facility for command line arguments? (i.e. the String[] array passed to main?)
You could then call your program like this:
java calc.textui.Calc my-nice-spreadsheet.data
=====================================================
Please write the follwoing in your calc.textui.Calc program immediately after the open brace of your class definition:
public class Calc ..... { // a line like this already exists
// insert next line here
public static String filename = System.getProperty("import");
// rest of your class, as before.
}
Then comment out the getProperty() line in your method that didn't work, but leave the rest including the System.out.println(filename);
Does it change?
Maybe system properties are not the most indicated way to do that (depends on your application).
You could also use command line arguments to pass the file name to your main method:
public class CommandLineExample {
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("usage: CommandLineExample <filename>");
System.exit(1);
}
String filename = args[0];
if (filename !=null && !filename.isEmpty()) {
...
}
}
}
Your program should be called as:
java CommandLineExample theFileName
the string "theFileName" will be passed to the main method in args[0] (any additional words will be passed in subsequent positions of args {args[1], args[2], ...})
EDIT
if the program must be called with
java -Dimport=filename ...
then System.getProperty("import") will return the filename.
Confirm that you are calling the correct program (class name, package, version, last compile was successful, ...) and also check that the property is not mistyped like java -Dinport=A-... or has additional spaces, uppercase letters...