I need to write JUnit tests for an old application that's poorly designed and is writing a lot of error messages to standard output. When the getResponse(String request) method behaves correctly it returns a XML response:
#BeforeClass
public static void setUpClass() throws Exception {
Properties queries = loadPropertiesFile("requests.properties");
Properties responses = loadPropertiesFile("responses.properties");
instance = new ResponseGenerator(queries, responses);
}
#Test
public void testGetResponse() {
String request = "<some>request</some>";
String expResult = "<some>response</some>";
String result = instance.getResponse(request);
assertEquals(expResult, result);
}
But when it gets malformed XML or does not understand the request it returns null and writes some stuff to standard output.
Is there any way to assert console output in JUnit? To catch cases like:
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
using ByteArrayOutputStream and System.setXXX is simple:
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;
#Before
public void setUpStreams() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
#After
public void restoreStreams() {
System.setOut(originalOut);
System.setErr(originalErr);
}
sample test cases:
#Test
public void out() {
System.out.print("hello");
assertEquals("hello", outContent.toString());
}
#Test
public void err() {
System.err.print("hello again");
assertEquals("hello again", errContent.toString());
}
I used this code to test the command line option (asserting that -version outputs the version string, etc etc)
Edit:
Prior versions of this answer called System.setOut(null) after the tests; This is the cause of NullPointerExceptions commenters refer to.
I know this is an old thread, but there is a nice library to do this: System Rules
Example from the docs:
public void MyTest {
#Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
#Test
public void overrideProperty() {
System.out.print("hello world");
assertEquals("hello world", systemOutRule.getLog());
}
}
It will also allow you to trap System.exit(-1) and other things that a command line tool would need to be tested for.
Instead of redirecting System.out, I would refactor the class that uses System.out.println() by passing a PrintStream as a collaborator and then using System.out in production and a Test Spy in the test. That is, use Dependency Injection to eliminate the direct use of the standard output stream.
In Production
ConsoleWriter writer = new ConsoleWriter(System.out));
In the Test
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
Discussion
This way the class under test becomes testable by a simple refactoring, without having the need for indirect redirection of the standard output or obscure interception with a system rule.
You can set the System.out print stream via setOut() (and for in and err). Can you redirect this to a print stream that records to a string, and then inspect that ? That would appear to be the simplest mechanism.
(I would advocate, at some stage, convert the app to some logging framework - but I suspect you already are aware of this!)
Slightly off topic, but in case some people (like me, when I first found this thread) might be interested in capturing log output via SLF4J, commons-testing's JUnit #Rule might help:
public class FooTest {
#Rule
public final ExpectedLogs logs = new ExpectedLogs() {{
captureFor(Foo.class, LogLevel.WARN);
}};
#Test
public void barShouldLogWarning() {
assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.
// Logic using the class you are capturing logs for:
Foo foo = new Foo();
assertThat(foo.bar(), is(not(nullValue())));
// Assert content of the captured logs:
assertThat(logs.isEmpty(), is(false));
assertThat(logs.contains("Your warning message here"), is(true));
}
}
Disclaimer:
I developed this library since I could not find any suitable solution for my own needs.
Only bindings for log4j, log4j2 and logback are available at the moment, but I am happy to add more.
If you were using Spring Boot (you mentioned that you're working with an old application, so you probably aren't but it might be of use to others), then you could use org.springframework.boot.test.rule.OutputCapture in the following manner:
#Rule
public OutputCapture outputCapture = new OutputCapture();
#Test
public void out() {
System.out.print("hello");
assertEquals(outputCapture.toString(), "hello");
}
#dfa answer is great, so I took it a step farther to make it possible to test blocks of ouput.
First I created TestHelper with a method captureOutput that accepts the annoymous class CaptureTest. The captureOutput method does the work of setting and tearing down the output streams. When the implementation of CaptureOutput's test method is called, it has access to the output generate for the test block.
Source for TestHelper:
public class TestHelper {
public static void captureOutput( CaptureTest test ) throws Exception {
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
ByteArrayOutputStream errContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
test.test( outContent, errContent );
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.out)));
}
}
abstract class CaptureTest {
public abstract void test( ByteArrayOutputStream outContent, ByteArrayOutputStream errContent ) throws Exception;
}
Note that TestHelper and CaptureTest are defined in the same file.
Then in your test, you can import the static captureOutput. Here is an example using JUnit:
// imports for junit
import static package.to.TestHelper.*;
public class SimpleTest {
#Test
public void testOutput() throws Exception {
captureOutput( new CaptureTest() {
#Override
public void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception {
// code that writes to System.out
assertEquals( "the expected output\n", outContent.toString() );
}
});
}
Based on #dfa's answer and another answer that shows how to test System.in, I would like to share my solution to give an input to a program and test its output.
As a reference, I use JUnit 4.12.
Let's say we have this program that simply replicates input to output:
import java.util.Scanner;
public class SimpleProgram {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print(scanner.next());
scanner.close();
}
}
To test it, we can use the following class:
import static org.junit.Assert.*;
import java.io.*;
import org.junit.*;
public class SimpleProgramTest {
private final InputStream systemIn = System.in;
private final PrintStream systemOut = System.out;
private ByteArrayInputStream testIn;
private ByteArrayOutputStream testOut;
#Before
public void setUpOutput() {
testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));
}
private void provideInput(String data) {
testIn = new ByteArrayInputStream(data.getBytes());
System.setIn(testIn);
}
private String getOutput() {
return testOut.toString();
}
#After
public void restoreSystemInputOutput() {
System.setIn(systemIn);
System.setOut(systemOut);
}
#Test
public void testCase1() {
final String testString = "Hello!";
provideInput(testString);
SimpleProgram.main(new String[0]);
assertEquals(testString, getOutput());
}
}
I won't explain much, because I believe the code is readable and I cited my sources.
When JUnit runs testCase1(), it is going to call the helper methods in the order they appear:
setUpOutput(), because of the #Before annotation
provideInput(String data), called from testCase1()
getOutput(), called from testCase1()
restoreSystemInputOutput(), because of the #After annotation
I didn't test System.err because I didn't need it, but it should be easy to implement, similar to testing System.out.
Full JUnit 5 example to test System.out (replace the when part):
package learning;
import static org.assertj.core.api.BDDAssertions.then;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class SystemOutLT {
private PrintStream originalSystemOut;
private ByteArrayOutputStream systemOutContent;
#BeforeEach
void redirectSystemOutStream() {
originalSystemOut = System.out;
// given
systemOutContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(systemOutContent));
}
#AfterEach
void restoreSystemOutStream() {
System.setOut(originalSystemOut);
}
#Test
void shouldPrintToSystemOut() {
// when
System.out.println("example");
then(systemOutContent.toString()).containsIgnoringCase("example");
}
}
You don't want to redirect the system.out stream because that redirects for the ENTIRE JVM. Anything else running on the JVM can get messed up. There are better ways to test input/output. Look into stubs/mocks.
If the function is printing to System.out, you can capture that output by using the System.setOut method to change System.out to go to a PrintStream provided by you. If you create a PrintStream connected to a ByteArrayOutputStream, then you can capture the output as a String.
// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
// IMPORTANT: Save the old System.out!
PrintStream old = System.out;
// Tell Java to use your special stream
System.setOut(ps);
// Print some output: goes to your special stream
System.out.println("Foofoofoo!");
// Put things back
System.out.flush();
System.setOut(old);
// Show what happened
System.out.println("Here: " + baos.toString());
for out
#Test
void it_prints_out() {
PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));
System.out.println("Hello World!");
assertEquals("Hello World!\r\n", out.toString());
System.setOut(save_out);
}
for err
#Test
void it_prints_err() {
PrintStream save_err=System.err;final ByteArrayOutputStream err= new ByteArrayOutputStream();System.setErr(new PrintStream(err));
System.err.println("Hello World!");
assertEquals("Hello World!\r\n", err.toString());
System.setErr(save_err);
}
Although this question is very old and has already very good answers I want to provide an alternative. I liked the answer of dfa however I wanted to have something reusable in different projects without copying the configuration and so I created a library out of it and wanted to contribute back to the community. It is called Console Captor and you can add it with the following snippet:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>consolecaptor</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
Example class
public class FooService {
public void sayHello() {
System.out.println("Keyboard not responding. Press any key to continue...");
System.err.println("Congratulations, you are pregnant!");
}
}
Unit test
import static org.assertj.core.api.Assertions.assertThat;
import nl.altindag.console.ConsoleCaptor;
import org.junit.jupiter.api.Test;
public class FooServiceTest {
#Test
public void captureStandardAndErrorOutput() {
ConsoleCaptor consoleCaptor = new ConsoleCaptor();
FooService fooService = new FooService();
fooService.sayHello();
assertThat(consoleCaptor.getStandardOutput()).contains("Keyboard not responding. Press any key to continue...");
assertThat(consoleCaptor.getErrorOutput()).contains("Congratulations, you are pregnant!");
consoleCaptor.close();
}
}
You cannot directly print by using system.out.println or using logger api while using JUnit. But if you want to check any values then you simply can use
Assert.assertEquals("value", str);
It will throw below assertion error:
java.lang.AssertionError: expected [21.92] but found [value]
Your value should be 21.92, Now if you will test using this value like below your test case will pass.
Assert.assertEquals(21.92, str);
Related
I just complete a Java CONSOLE application for Student Management.
I received a test case set (pdf file contains lines follow according to the requirements of the application) build based on the standard program (from my lecturer). You can overview what my app do and what is format of test casenter image description heree set in the attached image below.
The problem is that I want to use test cases for testing my app but instead of manually entering and matching line by line between Console IO and the pdf file => I want to write a program to automatically import and match the data between my jar/program to test cases.
However, I'm not sure how and where to start.
I have tried with google but unit test/white testing is still the thing that takes up all of my search. Hopefully in the process of continuing to try to search with google, someone will give me some suggestions or directions that will be useful to me. Thanks very much.
[My Program]
[Test cases set]
The way I'd do it is to decouple your application from the console so that you can use fake implementations for printing and reading from the console in your tests. "Fake" is the technical term - you can look up "test doubles" to learn about those and other related ideas. This idea is known as dependency injection, or the dependency inversion principle.
The way we do this is to use interfaces. Here's an example of an application that prints some items:
import java.util.List;
public class ItemPrinterApplication {
public ItemPrinterApplication(OutputWriter outputWriter, List<Item> items) {
this.outputWriter = outputWriter;
this.items = items;
}
public void run() {
outputWriter.writeLine("Name, Price");
items.forEach(item -> outputWriter.writeLine(item.name + ", " + item.price));
}
private OutputWriter outputWriter;
private List<Item> items;
}
OutputWriter is the thing responsible for the printing. It's just an interface, so the application doesn't know whether it writes to the console or somewhere else:
public interface OutputWriter {
void writeLine(String line);
}
For completeness, the Item class just holds some data:
public class Item {
public Item(String name, Integer price) {
this.name = name;
this.price = price;
}
public final String name;
public final Integer price;
}
I can then write a test using JUnit that checks that when I run this application, I get the output that I want. I do that by using an implementation of OutputWriter that just writes to a string. That way it's easy to check in the test:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
public class ItemPrinterTest {
#Test
public void itPrintsAListOfItems() {
List<Item> items =
List.of(
new Item("Apple", 50),
new Item("Carrot", 25),
new Item("Milk", 120)
);
FakeOutputWriter fakeOutputWriter = new FakeOutputWriter();
ItemPrinterApplication app = new ItemPrinterApplication(fakeOutputWriter, items);
app.run();
Assertions.assertEquals(
"Name, Price\n" +
"Apple, 50\n" +
"Carrot, 25\n" +
"Milk, 120\n",
fakeOutputWriter.written
);
}
}
and FakeOutputWriter looks like
public class FakeOutputWriter implements OutputWriter {
public String written = "";
#Override
public void writeLine(String line) {
written += line;
written += "\n";
}
}
This gives me confidence that I'm writing the output correctly. In main, though, I want to actually print to the console:
import java.util.List;
public class Main {
public static void main(String[] args) {
OutputWriter outputWriter = new ConsoleOutputWriter();
List<Item> items =
List.of(
new Item("Apple", 50),
new Item("Carrot", 25),
new Item("Milk", 120)
);
new ItemPrinterApplication(outputWriter, items).run();
}
}
and ConsoleOutputWriter does exactly that:
public class ConsoleOutputWriter implements OutputWriter{
#Override
public void writeLine(String line) {
System.out.println(line);
}
}
You could take the same approach for faking reading input. Your interface would have a function that takes no arguments and reads a string:
interface InputReader {
String readLine()
}
so in the tests you could fake that and in main, read using a Scanner or something.
This is a sample program:
public class FunctionalTest {
public int f(int r) {
int result = r * 5;
return result;
}
public static void main(String[] args) {
FunctionalTest funct = new FunctionalTest();
System.out.println(funct.f(5));
}
}
I'm a beginner.
How to write a functional test for this code?
How to write functional tests? Do I need to TestNG?
Is it enough to write the examination method?
Could someone explain to me and write a sample functional test for this program?
Well, if you're specifically asking for functional testing, there's not much you can do with that code snippet. You can do a unit test from the f method using JUnit like this:
#Test
public void testF(){
FunctionalTest t1 = new FunctionalTest();
assertEquals((t1.f(1) % 5), 0); //checks that is getting multiplied by 5.
}
However, you want functional testing, so by running your compiled app and assessing the result you're testing your app functionality by multiple units (AKA integration): your f method and your main method.
Regards!
First of all, you need to have a clear definition of contract you want to verify. From the code, I assume it is something like "the method should return the number equal to argument multiplied by 5".
TestNG, JUnit or other test frameworks is not mandatory for your case. The test may look like:
public void testF() {
int arg = 5;
int result = new FunctionalTest().f(arg);
assert result == arg * 5;
}
Also please keep in mind that to use assert you need JVM started with -ea flag.
Beware the terms you used:
the functional testing provide values to your user/customer
That implies:
black box testing: you have to test your whole system (hard+soft)
the test should target your user/customer needs (explicit report or test)
You can use whatever you want to test the feature (from unit test to jbehave).
In your case (using JUnit 4 and AssertJ):
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
/*
As an user
I want have 25 whatever I sent
*/
public class NumberGenerationTest {
private static final String PATH = "directory of your class file";
private InputStream stdout;
/* Nominal case */
#Test
public void shall_return_number_25_when_called_with_5() throws Exception {
when_I_call_FunctionalTest_with("5");
then_it_returns("25");
}
/* Nominal case or potential error case */
#Test
public void shall_return_number_25_when_called_with_10() throws Exception {
when_I_call_FunctionalTest_with("10");
then_it_returns("25");
}
/* Nominal case or potential error case */
#Test
public void shall_return_number_25_when_called_with_ABC() throws Exception {
when_I_call_FunctionalTest_with("ABC");
then_it_returns("25");
}
private void when_I_call_FunctionalTest_with(String parameter) throws Exception {
ProcessBuilder builder = new ProcessBuilder("java" ,"-classpath", PATH,"FunctionalTest" , parameter);
builder.redirectErrorStream(true);
Process process = builder.start();
stdout = process.getInputStream ();
}
private void then_it_returns(String expectedResult) throws Exception {
BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
String line = reader.readLine ();
Assertions.assertThat(line).isNotNull();
Assertions.assertThat(line).isEqualTo(expectedResult);
}
}
It seems you have an error in your main() ... or not.
I have problem mock whenNew(File.class) using PowerMockito. Here is my method I want to test:
public void foo() {
File tmpFile = new File("Folder");
if (!tmpFile.exists()) {
if (!configFolder.mkdir()) {
throw new RuntimeException("Can't create folder");
}
}
File oneFileInFolder = new File(tmpFile, "fileOne.txt");
if (oneFileInFolder.exists()){
//do something
}
}
Here is test code I wrote:
static File mockFile;
#Before
public void setUp() throws Exception {
//....some code
mockFolder = mock(File.class);
when(mockFolder.getPath()).thenReturn("Folder");
when(mockFolder.exists()).thenReturn(true);
whenNew(File.class).withParameterTypes(String.class).withArguments(anyString()).thenReturn(mockFolder);
//...some code
}
But when I debug my testcase, I still see a real folder created in my pwd. I don't want folders created when I run my testcases. Any idea?
Since you haven't specified this in your question, the following may be missing:
#PrepareForTest(ClassYoureCreatingTheFileInstanceIn.class)
According to the Wiki:
Note that you must prepare the class creating the new instance of MyClass for test, not the MyClass itself. E.g. if the class doing new MyClass() is called X then you'd have to do #PrepareForTest(X.class) in order for whenNew to work.
In other words, X is the class that contains foo() in your example.
public void createRootElement() throws FileNotFoundException, IOException
{
Properties prop = new Properties();
prop.load(new FileInputStream("/home/asdf/Desktop/test.properties"));
File file = new File(prop.getProperty("filefromroot"));
try
{
// if file doesn't exists, then create it
if (!file.exists())
{
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write("<root>"); //create the root tag for the XML File.
bw.close();
}
catch(Exception e)
{
writeLog(e.getMessage(),false);
}
}
I'm new to junit testing.I would like to know how to write test case for this and what all to be considered. how to call the method is called from this test.?
A JUnit Test Case should look like this:
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class ClassToBeTestedTest {
#Test
public void test() {
ClassToBeTested c = new ClassToBeTested();
c.createRootElement();
assertTrue(c.rootElementExists());
}
}
You mark the test method with the #Test annotation and write the code that executes what you want to test.
At this example, I created a instance of your class and called the createRootElement method.
After that, I made a assertion to verify if everything behaved like I expected.
There are many things you can assert. Read the JUnit documentation for more information.
A good practice is to write the test BEFORE you actually write the code. So the test will guide you on how to write a better code. This is called TDD. Google for it.
I have an application where I want to write unit tests to test the output, written to System.out (and perhaps System.err).
Each individual test works as expected, however when adding multiple tests in the same class some tests fail because JUnit4 appears to be multi-threaded (thus no guarantees exists on when exactly the stream is reset).
The same happens when I separate all test methods in their own class and use a test suite.
Any ideas?
private static final PrintStream SYS_OUT = System.out;
private static final PrintStream SYS_ERR = System.err;
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
#Before
public final void setUpStreams() throws UnsupportedEncodingException {
System.setOut(new PrintStream(this.outContent, true, CHARSET));
System.setErr(new PrintStream(this.errContent, true, CHARSET));
}
#After
public final void cleanUpStreams() {
System.setOut(SYS_OUT);
System.setErr(SYS_ERR);
}
#Test
public final void listServicesAndMethods() throws ServiceException, UnsupportedEncodingException {
com.example.Main.main(new String[]{"--list-services"});
LOG.debug(this.outContent.toString(CHARSET));
assertTrue("String not found", this.outContent.toString(CHARSET).contains("Some string"));
assertFalse("Other string found", this.outContent.toString(CHARSET).contains("Some other string"));
this.outContent.reset();
this.errContent.reset();
}
Edit: It turns out that the issue with the failing tests was not (just?) because of the streams, but due to the fact that I stored the options in static fields in my main class. This has the effect that several options stay active during consecutive tests. I realised that after implementing Arian's suggestion, I then used the second class as an instance, instead of calling static methods, thus solving my issue.
Thanks to all who replied.
If not already done so, rewrite the parts of your application to take the output as a parameter, instead of writing directly to System.out. This is usually better design regardless of testing.
In each test, create a new output stream (or spy on System.out if you must) and pass it to the code unit under test.