This question already has answers here:
How to test void method with Junit testing tools?
(6 answers)
Closed 9 years ago.
So I created a log function so I can see the date of every action. I now want to unittest them. Because the function uses a void, I can't test the output.
I might change the void to a String, but then the point of using it as a void would be gone!
What are my options?
public class Logger {
public static void log(String s) {
Date d = new Date();
SimpleDateFormat ft =
new SimpleDateFormat ("yyyy.MM.dd '-' hh:mm:ss");
System.out.println("[" + ft.format(d) + "]: " + s);
}
}
JUnit
#Test
public void testLoggerEmptyString() {
String s = "";
log(s);
}
Thanks :D
You need to test the side effects of your method. In this case, the side effect you want to observe is something getting written to System.out. You can use System.setOut to install your own OutputStream. From there you can verify that something was written to it.
For instance:
String s = "your message";
ByteArrayOutputStream sink = new ByteArrayOutputStream();
System.setOut(new PrintStream(sink, true));
log(s);
assertThat(new String(sink.toByteArray()), containsString(s));
You can use the library System Rules. It automatically sets the System.out back after the test.
public void MyTest {
#Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
#Test
public void testLoggerEmptyString() {
log("hello world");
assertEquals("hello world", log.getLog());
}
}
I might change the void to a String, but then the point of using it as a void would be gone!
I don't think so. If your method would look like this:
public static String getLogMessage(String s) {
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat ("yyyy.MM.dd '-' hh:mm:ss");
return String.format("[%s] %s", ft.format(d), s);
}
You could test the output and use it in your code with
System.out.println(Whatever.getLogMessage("Foo for the world"));
or even
System.err.println(Whatever.getLogMessage("Holy shit!"));
Related
I wrote a simple Custom Formatter for my logging that prints a DateTime in the specified format. Everything works fine, but the datetime doesn't get printed in my log file. Below is my CustomFormatter.java:
CustomFormatter.java:
public class CustomFormatter extends Formatter {
SimpleDateFormat sdf;
public CustomFormatter() {
sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
}
public String format(LogRecord rec) {
StringBuffer buf = new StringBuffer(1000);
buf.append(formatMessage(rec));
return buf.toString();
}
public String getHead(Handler h) {
return (sdf.format(new java.util.Date()) + ": \t");
}
public String getTail(Handler h) {
return "\n";
}
}
Im my main class, I initialize my logger as:
Main.java:
private static Logger logger = Logger.getLogger("org.somecompany.someproject");
public static void main(String[] args){
try{
String _pattern = "myLogger.log.%g";
int numLogFiles = 10;
int fileSize = 1000000;
boolean appendToFile = true;
FileHandler fh = new FileHandler(pattern, fileSize, numLogFiles, appendToFile);
fh.setFormatter(new CustomFormatter());
logger.addHandler(fh);
logger.setLevel(Level.ALL);
} catch(IOException i) { System.out.println("Unable to init logger");}
logger.info("Begin");
logger.info("Line 1");
logger.info("Line 2");
logger.info("End");
fh.close();
fh = null;
}
The log file should have a datetime printed at the beginning of each line and it isn't. Any help is appreciated.
I think you misunderstand the purpose of getHead() and getTail().
Those methods are not called per log entry - getHead() is called once before the Handler (FileHandler) logs the first entry and getTail() is called once before the Handler (FileHandler) closes the log stream.
This is used for example in the XMLFormatter to create a valid XML file (where there must be an XML start tag before the first log entry and an XML end tag after the last log entry to be valid XML).
If you look closely at the created log file this is exactly what happens: the log file creates a timestamp at the start and ends with a newline.
Note that adding time stamps to your log entries doesn't require writing a CustomFormatter. Properly configuring a SimpleFormatter is enough for your purpose.
I'm trying to parse a date departureTime:
String departureTime2, departureTime;
departureTime2 = departureTime = "2018-01-01 11:11:11.1";
Using the JUnit when method
when(bookingController.booking( departureTime, departureTime2).thenAnswer(new Answer<MockHttpSession>()
{
#Override
public MockHttpSession answer(InvocationOnMock invocation) throws Throwable
{
Object[] args = invocation.getArguments();
return (MockHttpSession) args[0];
}
});
My booking method in BookingController
#RequestMapping(value = "/bookingsingle", method = RequestMethod.POST)
public String bookingSingle(#RequestParam String departureTime,..)'
formats the date using
Date departureTimeAsDate = null;
DateFormat df = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss.S", Locale.ENGLISH);
try { departureTimeAsDate = df.parse(departureTime); } catch (ParseException e) { e.printStackTrace(); }
However I receive the error
java.text.ParseException: Unparseable date: "2018-01-01 11:11:11.1"
The date appears to be in the perfect format, and upon using the same code in a Java fiddle tool it compiles, however in intellij it runs into the error. Any suggestions would be appreciated.
EDIT:
I believe the problem may lie somewhere in the passing of the String in the when() method. Note that booking() has many irrelevant parameters I have excluded.
I have worked with junit test integration tests and controller tests in spring and usually we test the output of a method but when i tried to test a simple hello world in main method i had no idea how to go about it so will like to get any idea on what do write
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
This is the simple java class any idea how i can test it
I tried to write something like this
public void mainMethodTest() throws Exception{
System.out.println("hello world");
String[] args = null;
Assert.assertEquals(System.out.println("hello world"),App.main(args));
}
You could assign to the System.out variable a ByteArrayOutputStream object which you store the reference in a variable.
Then invoke your main() method and assert that the String content of the ByteArrayOutputStream object contains the expected String:
#Test
public void main() throws Exception{
PrintStream originalOut = System.out; // to have a way to undo the binding with your `ByteArrayOutputStream`
ByteArrayOutputStream bos = new ByteArrayOutputStream();
System.setOut(new PrintStream(bos));
// action
App.main(null);
// assertion
Assert.assertEquals("hello world", bos.toString());
// undo the binding in System
System.setOut(originalOut);
}
Why does it work ?
bos.toString() returns the "Hello World!" String passed in the method under test:
System.out.println( "Hello World!" );
as after setting System.out in this way : System.setOut(new PrintStream(bos));, the out variable refers to a PrintStream object that decorates the ByteArrayOutputStream object referenced by the bos variable.
So any System.out invocations will write bytes in the ByteArrayOutputStream object.
You can change your class this way
import java.io.PrintStream;
public class TestHelloWorld {
public final static void main(String[] args) {
doPrint(System.out);
}
static void doPrint(PrintStream ps) {
ps.println("Hello World");
}
}
and test the doPrint function by providing your own PrintStream you create around a ByteArrayOutputStream:
public void mainMethodTest() throws Exception{
ByteArrayOutputStream data = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(data, true, "UTF-8");
TestHelloWorld.doPrint(ps);
ps.flush();
Assert.assertEquals("Hello World") + System.getProperty("line.separator"), new String(data, "UTF-8"));
}
Another solution is to replace the system's PrintStream by your own:
System.setOut(new PrintStream(data, true, "UTF-8"));
but that's quite ugly and I try to avoid that. Above solution is more clear, easier to maintenance and you can be sure that no other part of a larger application is printing something to STDOUT while you do your test, leading to a failure of it.
You can run Junit from a main method if thats what you mean.
public static void main( String[] args )
{
JUnitCore junit = new JUnitCore();
Result result = null;
try {
result = junit.run(MyTestClass.class);
}
catch(Exception e)
{
e.printStackTrace();
}
int passed = result.getRunCount()-result.getFailureCount();
}
public class MyTestClass{
#Test
public void testAllBrowsers(){
//test code and asserts
}
}
Is it possible to force Properties not to add the date comment in front? I mean something like the first line here:
#Thu May 26 09:43:52 CEST 2011
main=pkg.ClientMain
args=myargs
I would like to get rid of it altogether. I need my config files to be diff-identical unless there is a meaningful change.
Guess not. This timestamp is printed in private method on Properties and there is no property to control that behaviour.
Only idea that comes to my mind: subclass Properties, overwrite store and copy/paste the content of the store0 method so that the date comment will not be printed.
Or - provide a custom BufferedWriter that prints all but the first line (which will fail if you add real comments, because custom comments are printed before the timestamp...)
Given the source code or Properties, no, it's not possible. BTW, since Properties is in fact a hash table and since its keys are thus not sorted, you can't rely on the properties to be always in the same order anyway.
I would use a custom algorithm to store the properties if I had this requirement. Use the source code of Properties as a starter.
Based on https://stackoverflow.com/a/6184414/242042 here is the implementation I have written that strips out the first line and sorts the keys.
public class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {
private boolean firstlineseen = false;
public StripFirstLineStream(final OutputStream out) {
super(out);
}
#Override
public void write(final int b) throws IOException {
if (firstlineseen) {
super.write(b);
} else if (b == '\n') {
firstlineseen = true;
}
}
}
private static final long serialVersionUID = 7567765340218227372L;
#Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(new TreeSet<>(super.keySet()));
}
#Override
public void store(final OutputStream out, final String comments) throws IOException {
super.store(new StripFirstLineStream(out), null);
}
}
Cleaning looks like this
final Properties props = new CleanProperties();
try (final Reader inStream = Files.newBufferedReader(file, Charset.forName("ISO-8859-1"))) {
props.load(inStream);
} catch (final MalformedInputException mie) {
throw new IOException("Malformed on " + file, mie);
}
if (props.isEmpty()) {
Files.delete(file);
return;
}
try (final OutputStream os = Files.newOutputStream(file)) {
props.store(os, "");
}
if you try to modify in the give xxx.conf file it will be useful.
The write method used to skip the First line (#Thu May 26 09:43:52 CEST 2011) in the store method. The write method run till the end of the first line. after it will run normally.
public class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {
private boolean firstlineseen = false;
public StripFirstLineStream(final OutputStream out) {
super(out);
}
#Override
public void write(final int b) throws IOException {
if (firstlineseen) {
super.write(b);
} else if (b == '\n') {
// Used to go to next line if did use this line
// you will get the continues output from the give file
super.write('\n');
firstlineseen = true;
}
}
}
private static final long serialVersionUID = 7567765340218227372L;
#Override
public synchronized Enumeration<java.lang.Object> keys() {
return Collections.enumeration(new TreeSet<>(super.keySet()));
}
#Override
public void store(final OutputStream out, final String comments)
throws IOException {
super.store(new StripFirstLineStream(out), null);
}
}
Can you not just flag up in your application somewhere when a meaningful configuration change takes place and only write the file if that is set?
You might want to look into Commons Configuration which has a bit more flexibility when it comes to writing and reading things like properties files. In particular, it has methods which attempt to write the exact same properties file (including spacing, comments etc) as the existing properties file.
You can handle this question by following this Stack Overflow post to retain order:
Write in a standard order:
How can I write Java properties in a defined order?
Then write the properties to a string and remove the comments as needed. Finally write to a file.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
properties.store(baos,null);
String propertiesData = baos.toString(StandardCharsets.UTF_8.name());
propertiesData = propertiesData.replaceAll("^#.*(\r|\n)+",""); // remove all comments
FileUtils.writeStringToFile(fileTarget,propertiesData,StandardCharsets.UTF_8);
// you may want to validate the file is readable by reloading and doing tests to validate the expected number of keys matches
InputStream is = new FileInputStream(fileTarget);
Properties testResult = new Properties();
testResult.load(is);
I have this GUI program where I'm trying to basically copy windows CMD. Since I have lots of features in this program, I decided to put parts of the code in different classes. But it doesn't respond.
if(command.size()<2 && command.size()>0) {
switch(command.get(0)) {
case "dt":
getDateTime a = new getDateTime();
a.Start();
break;
// other case(s) down below
}
}
Here is the geDateTime class
public class getDateTime {
public static void Start() {
Terminal t = new Terminal();
try {
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date = new Date();
String s = dateFormat.format(date).toString();
t.print(s);
}catch(Exception e){ e.printStackTrace(); }
}
}
Here is the print(); void in the main class...
public static void print(String s) {
Color c = Color.WHITE; // prints white text to JFrame
Style style = output.addStyle("Style", null);
StyleConstants.setForeground(style, c);
try{
document.insertString(document.getLength(), s, style);
}catch(Exception e){e.printStackTrace();}
}
Now when I enter the command for accessing the getDateTime class, the program freezes and I can't input anything. HOWEVER, if I just put the getDateTime class into a void inside the main class it works fine; but this would be a problem to just put everything into the main class since some function(s) could have hundreds of line of code.
No errors are produced when the program freezes.
In the code snippet that you have earlier, the code was trying to create a new Terminal rather than using the existing one.
Try this:
private static void print() {
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date = new Date();
String s = dateFormat.format(date).toString();
print(s);
}
In the access method:
case "dt":
print();
break;
Update: On a side note, try to avoid static if at all possible. Generally speaking, it's bad practice. See https://stackoverflow.com/a/7026563/1216965