I have been asked to introduce unit test in a legacy Java Application that runs and operates from Command Line. Basically the main loop prints out a Menu, the user inputs something and it shows more data.
This Main class illustrate how the application works.
public class Main{
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String argv[]) throws IOException{
while (true) {
char input = (char) reader.read();
if(input == 'x'){
return;
}
System.out.println(input);
}
}
}
I'd like my test methods to look something like this
public void testCaseOne(){
Main.main();
String result = "";
result = sendInput("1");
assertEqual(result, "1");
result = sendInput("x");
assertEqual(result,"");
}
I am aware of the System.setOut() and System.setIn() methods, but I cannot figure out a way to make the System.setIn() method work in this context, since the reader.read() method is blocking my thread.
Is my test design wrong?
Is there a way to design the sendInput() method to work through the blocking reader.read() call?
I would suggest refactoring the code to allow the input/output streams to be injected, and then you can mock them. If you couuld change it to something like
public class Main{
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String argv[]) throws IOException{
new YourClass(reader,System.out).run();
}
}
public class YourClass { // I don't know what your class is actually doing, but name it something appropriate
private final InputReader reader;
private final PrintStream output;
public YourClass(InputReader reader, PrintStream output) {
this.reader = reader;
this.output = ouptut;
}
public void run() {
while (true) {
char input = (char) reader.read();
if(input == 'x')
return;
output.println(input);
}
}
This design does a couple of things:
It takes the logic out of your main class. Typically a main method is really just used for launching an application.
It makes YourClass more easily unit testable. In your tests, you can simply mock out the input/output.
Edit: Update to how this refactoring helps with the blocking IO problem
By making the reader/output injectable as shows above, you don't actually need to use the real System.in and System.out - you can use a mock instead. This eliminates the need to actually have blocking reads.
public void testCaseOne(){
// pseudocode for the mock - this will vary depending on your mock framework
InputReader reader = createMock(InputReader);
// the first time you read it will be a "1", the next time it will be an "x"
expect(reader.read()).andReturn("1");
expect(reader.read()).andReturn("x");
PrintStream stream = createMock(PrintStream);
// only expect the "1" to get written. the "x" is the exit signal
expect(stream.println("1"));
new YourClass(reader,stream).run();
verifyMocks();
}
I would refactor Main so it's easier to test.. like so:
public class Main{
private boolean quit = false;
public static void main(String[] argv) throws IOException {
Main main = new Main();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
char input = main.readInput(reader);
while (!main.quit()) {
System.out.println(input);
input = main.readInput(reader);
}
}
public char readInput(Reader reader) throws IOException{
char input = (char) reader.read();
if(input == 'x'){
quit = true;
return '\0';
}
return input;
}
public boolean quit(){
return quit;
}
}
Personally, I try to stay away from static variables. If you need one you could always declare it in the main method like above.
Testing the while(true) is pretty much impossible because testing if the while loop never quits would take an infinite amount of time. Then there is the question if you should test the quitting of the loop in the main.quit() == true case. Personally, I would just test the core logic and leave the rest untested:
public class MainTest {
private Main main;
#Before
public void setup(){
main = new Main();
}
#Test
public void testCaseOne() throws IOException{
char result1 = main.readInput(new StringReader("1"));
assertEquals(result1, '1');
assertFalse(main.quit());
char result2 = main.readInput(new StringReader("x"));
assertEquals(result2, '\0');
assertTrue(main.quit());
}
}
Here is the solution I went with that required no refactoring of the legacy code.
In a nutshell, I made an Abstract Test Class that compiles and execute the Application in a Process on a seperate thread. I attach myself to the Input/Output of the Process and read/write to it.
public abstract class AbstractTest extends TestCase{
private Process process;
private BufferedReader input;
private BufferedWriter output;
public AbstractTest() {
//Makes a text file with all of my .java files for the Java Compiler process
Process pDir = new ProcessBuilder("cmd.exe", "/C", "dir /s /B *.java > sources.txt").start();
pDir.waitFor();
//Compiles the application
Process p = new ProcessBuilder("cmd.exe", "/C", "javac #sources.txt").start();
p.waitFor();
}
protected void start(){
Thread thread = new Thread() {
public void run() {
//Execute the application
String command = "java -cp src/main packagename.Main ";
AbstractTest.this.process = = new ProcessBuilder("cmd.exe", "/C", command).start();
AbstractTest.this.input = new BufferedReader(new InputStreamReader(AbstractTest.this.process.getInputStream()));
AbstractTest.this.output = new BufferedWriter(new OutputStreamWriter(AbstractTest.this.process.getOutputStream()));
}
}
}
protected String write(String data) {
output.write(data + "\n");
output.flush();
return read();
}
protected String read(){
//use input.read() and read until it makes senses
}
protected void tearDown() {
this.process.destroy();
this.process.waitFor();
this.input.close();
this.output.close();
}
}
Afterward, it was pretty easy to make actual test class and implement real test methods.
public void testOption3A(){
start();
String response = write("3");
response = write("733");
assertEquals("*** Cactus ID 733 not found ***",response);
}
Pros
No refactoring needed
Actually testing the implementation (No Mocking/Injection)
Doesn't require any external librairies
Cons
Pretty hard to debug when things aren't working proprely (Fixable)
Rely heavily on OS behavior (Windows in this class, but Fixable)
Compiles the application for every test class (Fixable I think?)
"Memory Leak" when there is an error and the process is not killed
(Fixable I think?)
This is probably a borderline "hack", but it met my needs and demands.
Related
import java.io.IOException;
import java.util.*;
public class Owner {
public static Scanner sc = new Scanner(System.in);
public static void main(String args[]) throws IOException {
String n = sc.nextLine();
Info.namec(n);
}
}
This is the second class, which is supposed to be printing the "HELLO" in the text file.
import java.io.*;
public class Info {
public static void namec(String n) throws IOException//name check
{
File f = new File("TEXT");
FileWriter fw = new FileWriter(f);
fw.write("HELLO!");
}
}
This code is not working, nothing is being typed in the text file.
There are 2 classes, the "Hello" is not being printed.
You don't close the file, and it looks like there is some buffering going on, so nothing gets to the file, as it's so short. Try this:
public static void namec(String n) throws IOException {
File f = new File("TEXT");
try (FileWriter fw = new FileWriter(f)) {
fw.write("HELLO!");
}
}
The so called try-with-resources statement will automatically close the stuff that gets opened in try(), which is generally desirable.
Calling fw.write("String") alone does not guarantee data will be written to file. The data may simply be written to a cache and never get written to the actual file on disk.
I suggest you use below methods,
fw.flush() - Call this when you want data you just wrote to be reflected in the actual file.
fw.close() - Call this when you are done writing all data that needs to be written by calling write method.
Whenever you are using file writer it stores data on cache so you will require for flush and close file writer object.
Here i am adding sample code hope that will help you.
package com.testfilewriter;
import java.io.FileWriter;
public class FileWriterExample {
public static void main(String args[]) {
try {
FileWriter fw = new FileWriter("D:\\testfile.txt");
fw.write("Welcome to stack overflow.");
fw.close();
} catch (Exception e) {
System.out.println(e);
}
System.out.println("File writing complete.");
}
}
I have a multithreaded program, which is showing weird behavior in one certain condition. Due to the proprietary issue, I cannot post the complete code here, but I am putting the example bug here.
I have one class which has a boolean variable and is extending Java Thread class
public class SystemStreamCapture extends Thread {
InputStream is;
boolean done = false;
List<String> buffer;
private final static Logger Log = Logger.getLogger(SystemStreamCapture.class);
public SystemStreamCapture(InputStream is) {
this.is = is;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
this.buffer.add(line);
}
br.close();
isr.close()
} catch (IOException ioe) {
Log.error(ioe);
} finally {
this.done = true;
}
}
public List<String> getData() {
return this.buffer;
}
public boolean isDone() {
return this.done;
}
}
This Thread is used inside other class as follows:
public class FileReader {
private final static Logger Log = Logger.getLogger(FileReader.class);
public List<String> readLines(FileLineRequestModel request) throws IOException {
String[] script = { "/bin/sh", "-c", request.getCommand() };
Log.debug("executing line fetch command : " + request.getCommand());
Process p = Runtime.getRuntime().exec(script);
SystemStreamCapture errStream = new SystemStreamCapture(p.getErrorStream());
SystemStreamCapture outStream = new SystemStreamCapture(p.getInputStream());
errStream.start();
outStream.start();
while (!outStream.isDone()) {
// keep looping
//Log.debug("reading...");
}
return outStream.getData();
}
The weird part is while loop in the later class. Loop never ends despite "done" variable getting "true". I also tried syntax '(outStream.isDone() == false)', just to see whether it makes any difference or not (which I know will not).
However as soon as I turn 'Log.debug("reading...")' inside while, it works as expected.
I have no idea whats happening, maybe outStream.isDone() call tries to copy boolean into new memory address and the relentless polling is not allowing it.
Please share your experience and knowledge on this issue.
In multi-threaded environment you required visibility when one thread change one value should visible to other thread.For your case you can use volatile boolean flag or use AtomicBoolean variable type so it will visible to other thread when one thread changes the value.
volatile boolean done = false;
In some of the Java classes I see the IO resources declared as instance varibles and are being used in multiple methods.How can I close them?Few Suggested finalize() and they also say that it is not recommended. May I know if there is any better approach for this.?
Ex:
public class test{
private PrintWriter writer=null;
public test(){
createWriter();
}
public void log(){
writer.write("test");
writer.flush();
}
public void createWriter(){
writer=new PrintWriter(new BufferedWriter(new FileWriter("file")));
}
}
Implements AutoCloseable in your class and override close() method and close all your IO related resources in this close() method.
Now if you are using Java 7 you can create a reference to your class using try with resource and JVM will automatically call close method of your class.
As you can see in the code of FilterReader class,
public abstract class FilterReader extends Reader {
protected Reader in;
//......Other code, and then
public void close() throws IOException {
in.close();
}
}
And if you write
try(FileReader fr = new FileReader("filename")){
// your code
}
and you are done JVM will automatically close it
There shoud be some kind of destructor. In junit for example (as you are naming your class "test") you have #AfterClass annotation to do your clean-up in such annotated method.
You just close it manually after you use it.
public class PrintWriterDemo {
private PrintWriter writer;
public PrintWriterDemo() {
writer = new PrintWriter(System.out);
}
public void log(String msg) {
writer.write(msg + "\n");
writer.flush();
}
public void close() {
System.out.println("print writer closed.");
writer.close();
}
public static void main(String[] args) {
PrintWriterDemo demo = new PrintWriterDemo();
demo.log("hello world");
demo.close();
}
}
I have a set of Java files in the same package, each having main methods. I now want the main methods of each of the classes to be invoked from another class step by step. One such class file is Splitter.java. Here is its code.
public static void main(String[] args) throws IOException {
InputStream modelIn = new FileInputStream("C:\\Program Files\\Java\\jre7\\bin\\en-sent.bin");
FileInputStream fin = new FileInputStream("C:\\Users\\dell\\Desktop\\input.txt");
DataInputStream in = new DataInputStream(fin);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine = br.readLine();
System.out.println(strLine);
try {
SentenceModel model = new SentenceModel(modelIn);
SentenceDetectorME sentenceDetector = new SentenceDetectorME(model);
String sentences[] = sentenceDetector.sentDetect(strLine);
System.out.println(sentences.length);
for (int i = 0; i < sentences.length; i++) {
System.out.println(sentences[i]);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (modelIn != null) {
try {
modelIn.close();
} catch (IOException e) {
}
}
fin.close();
}
}
I now want this to be invoked in AllMethods.java inside a main method.
So how can I do this? There are several other class files having main methods with IOException which have to be invoked in AllMethods.java file.
Update -
I have main methods having IOException as well as main methods not having IOEXception that has to be invoked in AllMethods.java.
You can do that. Main method is also just like any other static method
public static void main(String[] args) throws IOException {
....// do all the stuff
Splitter.main(args); // or null if no args you need
}
First of all, what you should probably do is refactor your code so each main method calls some other method, and then AllMethods makes calls to those new methods. I can imagine there might be some cases where it's useful if you're just trying to, for example, write some test code, but usually you wouldn't want to call main methods directly. It's just harder to read.
If you want to try it though, it's pretty easy, you just call the main method like any other static method. I once in college wrote a web server where, to handle authentication, I recursed on the main method. I think I got a C because it was unreadable code, but I had fun writing it.
class AllMethods {
public void callsMain() {
String[] args = new String[0];
Splitter.main(args);
}
}
In the Main.java, the main method should add throws Exception as shown below:
package com.company;
import java.io.FileNotFoundException;
public class Main extends makeFile {
public static void main(String[] args) throws FileNotFoundException {
makeFile callMakeFile = new makeFile();
makeFile.main(args);
// cannot figure out how to call the main method from the makeFile class here...
}
}
I have something like this:
public static final String path;
static {
path = loadProperties("config.conf").getProperty("path");
}
public static void main(String... args) {
// ... do stuff (starting threads that reads the final path variable)
// someone want's to update the path (in the config.conf file)
restart(); // ???
}
I want to reinitialize the JVM calling the static initializer again, and then main(...)!
Can it be done?
You can start your application using a custom class loader, this will allow you to load and unload your static variables.
However, basically its a very bad design to need to do this. I like making fields final, but you shouldn't make them final if you want to change them.
If your goal is simply to reload some configuration files, why not implement a file change monitor?
Here's a good tutorial on this subject:
http://download.oracle.com/javase/tutorial/essential/io/notification.html
I think what you're proposing (restarting your application automatically) would be a little more cumbersome than just watching for file updates.
A simpler approach is simply not to use the static initializer for this. Why not just make path non-final and load it in main?
I'm accepting Peter Lawrey answer but post a complete example for anyone to use!
I'm not going to use this in production code... there are other ways of doing it!
public class Test {
public static void main(String args[]) throws Exception {
start();
Thread.sleep(123);
start();
}
private static void start() throws Exception {
ClassLoader cl = new ClassLoader(null) {
protected java.lang.Class<?> findClass(String name)
throws ClassNotFoundException {
try{
String c = name.replace('.', File.separatorChar) +".class";
URL u = ClassLoader.getSystemResource(c);
String classPath = ((String) u.getFile()).substring(1);
File f = new File(classPath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte buff[] = new byte[(int) f.length()];
dis.readFully(buff);
dis.close();
return defineClass(name, buff, 0, buff.length, null);
} catch(Exception e){
throw new ClassNotFoundException(e.getMessage(), e);
}
}
};
Class<?> t = cl.loadClass("Test$Restartable");
Object[] args = new Object[] { new String[0] };
t.getMethod("main", new String[0].getClass()).invoke(null, args);
}
public static class Restartable {
private static final long argument = System.currentTimeMillis();
public static void main(String args[]) throws Exception {
System.out.println(argument);
}
}
}
how about this structure
public static void main(String... args) {
boolean restart = true;
while (restart )
{
retart = runApplication();
}
}
If you ever detect the need to restart you application, have runApplication return true.
If it is time to exit return false;
If you have an UI or a daemon so you can control output to stdout, you can make a wrapper on the outside that starts your program.
If the program upon exit outputs "RESTART" you can restart your program again from this wrapper. If not, it just ends.
Or if you want the pure java way, you can go with a solution with classloaders as Peter Lawrey mentioned in his post. Before going down this route you should really rethink your design (if it is your code) and make your code capable of cleaning itself up.