I am trying to write test methods in Intellij with jUnit. I can succesfully write to the file, however, I need to show that I can write a test method for this. I push in the write direction would be great. My ServerController class write to a file, checks a file for a username, and verifies username and password. This project is for a class and it appears that the most important lesson is learning about documentation (requirements, design, and testing). So, here I am trying to test.
package appLayer;
import java.io.*;
import java.util.Scanner;
public class ServerController {
public void writetoFile(String writeUsername, String writePassword) throws IOException {
PrintWriter fileWriting = new PrintWriter(new FileOutputStream("/Users/dannielsotelo/Documents/database.txt", true));
fileWriting.println(writeUsername + "," + writePassword); //
System.out.println(writeUsername + " was saved to database.txt");
fileWriting.close();
}
public boolean findUsername(String fUsername) {
File file = new File("/Users/dannielsotelo/Documents/database.txt");
try{
Scanner scanner = new Scanner(file);
int lineNum = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lineNum++;
if(line.contains(fUsername))
return true;
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return false;
}
public boolean verifyCredentials(String lUsername, String lPassword) {
File file = new File("/Users/dannielsotelo/Documents/database.txt");
try{
Scanner scanner = new Scanner(file);
int lineNum = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lineNum++;
if(line.contains(lUsername) && line.contains(lPassword))
return true;
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return false;
}
}
and my ServerControllerTest class
package appLayer;
import org.junit.Test;
import static org.junit.Assert.*;
public class ServerControllerTest {
#Test
public void testwritetoFile() {
}
#Test
public void findUsername() {
}
#Test
public void verifyCredentials() {
}
}
First, you should get rid of the redundant code when scanning the files. It would be sufficient to have one method that searches for arbitrary strings since technically it doesn't matter if you search for a username or a password.
Second, for testing purposes you really should not hard-code the database file. When writing JUnit tests you don't want your actual file to be written or read. Pass the file or the path to it as a constructor parameter.
Writing a test method is quite straight-forward. Write a value to the database, then read it and check if the previously written data is there. It's a good idea to start with a blank file for every test.
PS.: In terms of security, the approach of checking if a line contains username and password is a little disaster :D
Search this forum for testing void methods. But anyway your code is not really testable in sensible way. While you can test methods that return some value, you don't give it enough starting conditions to be sure they do what you want.
Don't hardcode path to files in the method, you'll be better off if it's an argument to the method. Then in tests you can be sure that if you create file with a specified content the method will do this or that or throw an exception if you don't create the file at all.
Related
I have a Java command-line program. I would like to create JUnit test case to be able to simulate System.in. Because when my program runs it will get into the while loop and waits for input from users. How do I simulate that in JUnit?
Thanks
It is technically possible to switch System.in, but in general, it would be more robust not to call it directly in your code, but add a layer of indirection so the input source is controlled from one point in your application. Exactly how you do that is an implementation detail - the suggestions of dependency injection are fine, but you don't necessarily need to introduce 3rd party frameworks; you could pass round an I/O context from the calling code, for example.
How to switch System.in:
String data = "Hello, World!\r\n";
InputStream stdin = System.in;
try {
System.setIn(new ByteArrayInputStream(data.getBytes()));
Scanner scanner = new Scanner(System.in);
System.out.println(scanner.nextLine());
} finally {
System.setIn(stdin);
}
Based on #McDowell's answer and another answer that shows how to test System.out, 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.
There are a few ways to approach this. The most complete way is to pass in an InputStream while running the class under test which is a fake InputStream which passes simulated data to your class. You can look at a dependency injection framework (such as Google Guice) if you need to do this a lot in your code, but the simple way is:
public class MyClass {
private InputStream systemIn;
public MyClass() {
this(System.in);
}
public MyClass(InputStream in) {
systemIn = in;
}
}
Under test you would call the constructor that takes the input stream. You cloud even make that constructor package private and put the test in the same package, so that other code would not generally consider using it.
Try to refactor your code to use dependency injection. Instead of having your a method that uses System.in directly, have the method accept an InputStream as an argument. Then in your junit test, you'll be able to pass a test InputStream implementation in place of System.in.
You can write a clear test for the command line interface by using the TextFromStandardInputStream rule of the System Rules library.
public void MyTest {
#Rule
public final TextFromStandardInputStream systemInMock
= emptyStandardInputStream();
#Test
public void readTextFromStandardInputStream() {
systemInMock.provideLines("foo");
Scanner scanner = new Scanner(System.in);
assertEquals("foo", scanner.nextLine());
}
}
Full disclosure: I'm the author of that library.
You could create a custom InputStream and attach it to the System class
class FakeInputStream extends InputStream {
public int read() {
return -1;
}
}
And then use it with your Scanner
System.in = new FakeInputStream();
Before:
InputStream in = System.in;
...
Scanner scanner = new Scanner( in );
After:
InputStream in = new FakeInputStream();
...
Scanner scanner = new Scanner( in );
Although I think you should better to test how your class should work with the data read from the input stream and not really how it reads from there.
The problem with BufferedReader.readLine() is that it is a blocking method which waits for user input. It seems to me that you don't particularly want to simulate that (i.e. you want tests to be fast). But in a testing context it continually returns null at high speed during testing, which is irksome.
For a purist you can make the getInputLine below package-private, and mock it: easy-peezy.
String getInputLine() throws Exception {
return br.readLine();
}
... you'd have to make sure that you had a way of stopping (typically) a loop of user interaction with the app. You'd also have to cope with the fact that your "input lines" would always be the same until you somehow changed the doReturn of your mock: hardly typical of user input.
For a non-purist who wishes to make life easy for themselves (and produce readable tests) you could put all this stuff below in your app code:
private Deque<String> inputLinesDeque;
void setInputLines(List<String> inputLines) {
inputLinesDeque = new ArrayDeque<String>(inputLines);
}
private String getInputLine() throws Exception {
if (inputLinesDeque == null) {
// ... i.e. normal case, during app run: this is then a blocking method
return br.readLine();
}
String nextLine = null;
try {
nextLine = inputLinesDeque.pop();
} catch (NoSuchElementException e) {
// when the Deque runs dry the line returned is a "poison pill",
// signalling to the caller method that the input is finished
return "q";
}
return nextLine;
}
... in your test you might then go like this:
consoleHandler.setInputLines( Arrays.asList( new String[]{ "first input line", "second input line" }));
before triggering off the method in this "ConsoleHandler" class which needs input lines.
maybe like this (not tested):
InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in));
in.write("text".getBytes("utf-8"));
System.setIn( save_in );
more parts:
//PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));
InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in));
//start something that reads stdin probably in a new thread
// Thread thread=new Thread(new Runnable() {
// #Override
// public void run() {
// CoursesApiApp.main(new String[]{});
// }
// });
// thread.start();
//maybe wait or read the output
// for(int limit=0; limit<60 && not_ready ; limit++)
// {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
in.write("text".getBytes("utf-8"));
System.setIn( save_in );
//System.setOut(save_out);
#Stefan Birkner, Thanks!
Modify Pom.xml
Ref:
https://stackoverflow.com/a/66127606/8317677
https://github.com/stefanbirkner/system-lambda/blob/master/pom.xml
https://github.com/stefanbirkner/system-lambda/blob/master/src/test/java/com/github/stefanbirkner/systemlambda/WithTextFromSystemInTest.java
<properties>
<system-lambda.version>1.2.1</system-lambda.version>
</properties>
<dependencies>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>${system-lambda.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Add function code
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class SimpleProgram003 {
public static void main(String[] args) {
try{
String c;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
do{
c = in.readLine();
System.out.println(c);
String d = c;
}while(!c.equals("q"));
}catch(Exception e){
System.out.println("catch Exception");
}
}
}
Add test code
import static com.github.stefanbirkner.systemlambda.SystemLambda.*;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Unit test for simple App. JUnit 4.x.
*/
public class SimpleProgram003Test {
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 setInput(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 1\nq\n";
setInput(testString);
SimpleProgram003.main(new String[0]);
// String a = getOutput();
assertEquals("Hello 1\r\nq\r\n", getOutput());
}
#Test // Multiply inputs
public void testCase2() throws Exception {
withTextFromSystemIn(
"Input1",
"Input2",
"q",
"Input3"
).execute(() -> {
SimpleProgram003.main(new String[0]);
// String a = getOutput();
assertEquals("Input1\r\nInput2\r\nq\r\n", getOutput());
});
}
}
My professor really threw us into this project with a blindfold on. We didn't go into depth on using and inserting files into Java. I'm getting a ton of errors, which are most likely due to my incorrect insertion of the file. I saved the text file in the same place the class file is saved on my computer, assuming that would be necessary. I've moved it around multiple places on my computer trying to get it to work. Here is the main program. I'm sorry if it's completely incorrect.
To explain what we're supposed to be doing further, here is the link to the prompt with the pseudocode. I haven't attempted to do all the actions listed because I haven't gotten the file to insert correctly yet.
http://jcsites.juniata.edu/faculty/rhodes/cs1/projects/program9Gen.html
Edit: This is the whole program in its glory. The class was created in a separate project as our introduction to Java classes. We were just told to use it again and insert the main program at the bottom just for ease of grading.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class GenSeq
{
private String seq;
private String species;
private String startCodon;
private String stopCodon;
private String shape;
private String chromosomeLocation;
public GenSeq (String seq, String species, String startCodon, String stopCodon,
String shape, String chromosomeLocation){
this.seq = seq;
this.species = species;
this.startCodon = startCodon;
this.stopCodon = stopCodon;
this.shape = shape;
this.chromosomeLocation = chromosomeLocation;
}
//Allowing the program to later set constructors
//Creating all the appropriate getters and setters for the instance variables
public void setSpecies(String newSpecies){
species = newSpecies;
}
public String getSpecies(){
return species;
}
public void setStartCodon(String newStartCodon){
startCodon = newStartCodon;
}
public String getStartCodon(){
return startCodon;
}
public void setStopCodon(String newStopCodon){
stopCodon = newStopCodon;
}
public String getStopCodon(){
return stopCodon;
}
public void setShape(String newShape){
shape = newShape;
}
public String getShape(){
return shape;
}
public void setChromosomeLocation(String newChromosomeLocation){
chromosomeLocation = newChromosomeLocation;
}
public String getChromosomeLocation(){
return chromosomeLocation;
}
public String toString(){
return "Sequence length: " + seq.length() +
"\nSpecies: "+ species +
"\nStart Codon: "+ startCodon +
"\nStart Codon: "+ stopCodon+
"\nShape: "+ shape +
"\nChromosomal Location: " + chromosomeLocation;
//Creating a toString method to hold all the class data
}
}
public static void main (String args[ ])
{
GenSeq seqA = null;
//Setting constructor to default if not set
//Opening the file
Scanner inputStream = null;
String seq;
try
{
inputStream = new Scanner (new File ("W:\jcsites.junata.edu\students\morrian14\seq.txt"));
}
catch (FileNotFoundException e)
{
System.out.println ("Error opening the file ");
System.exit (0);
}
do{
inputStream = inputStream.trim();
if ('>' == inputStream.charAt(0)){
seq = inputStream.nextLine();
}
}
while (inputStream.hasNextLine());
inputStream.close();
}
The error is this same one repeated continuously
File: C:\LEXIPC\Users\Alexis\GenSeq.java [line: 83]
Error: class, interface, or enum expected
One obvious issue, the last line is clearly meant to have been written as inputStream.close(); and not input.Stream.close(); you will probably need a try .. catch ... around closing the stream too
What exactly is your question? A few notes though...
Get rid of the do{} while() and just do something like this:
while(inputStream.hasNextLine(){
if('>' == inputStream.charAt(0))
seq = inputStream.nextLine();
}
inputStream.close();
I am a bit confused as to why you are recycling seq to read from the file, as that is what you are using as your file's name. A better way to do this would be to use a File class for your file names. Consider: File seq = new File(.../filename.txt).
Also, if you find that you are using too many try/catch blocks, consider using an exception handling class to clean up your code.
I made a windows service from a jar file using WinRun4J, so far it's very basic.
package org.boris.winrun4j.test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import org.boris.winrun4j.Service;
import org.boris.winrun4j.ServiceException;
public class ServiceWrite implements Service
{
private volatile boolean shutdown = false;
public int serviceMain(String[] args) throws ServiceException {
int count = 0;
while (!shutdown) {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
}
try {
FileWriter fstream = new FileWriter("result.txt");
BufferedWriter out = new BufferedWriter(fstream);
out.write("Counts: " + count);
out.close();
} catch (Exception e){
}
count++;
}
return 0;
}
public int serviceRequest(int control) throws ServiceException {
switch (control) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
shutdown = true;
break;
}
return 0;
}
}
When the service is started it just keeps writing every couple of seconds to result.txt located in the root folder.. (Just for trying out WinRun4J)
Now my question is, can I do a method in the service jar, like this
public void write(String s){
//Write some string s to result.txt
}
And then invoke this method from a different java file on the system, i.e
java WriteToFile SomeString
Where WriteToFile is supposed to invoke write with some argument.
Is it possible? if so, how ?
The overall purpose of this is to have a service running where I can invoke methods via a GUI.
to "invoke methods via a GUI", you can't do it with WinRun4J.
in general rule, a Windows Service can't have a GUI for security reason (except for special cases).
However, there are other tools to create a windows service from a Java application, with which it will be possible to have a service with GUI and able to interact with the Desktop.
I have the following class:
public class FileLoader {
private Map<Brand, String> termsOfUseText = new HashMap<Brand, String>();
public void load() {
for (Brand brand : Brand.values()) {
readAndStoreTermsOfUseForBrand(brand);
}
}
private void readAndStoreTermsOfUseForBrand(Brand brand) {
String resourceName = "termsOfUse/" + brand.name().toLowerCase() + ".txt";
InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
try {
String content = IOUtils.toString(in);
termsOfUseText.put(brand, content);
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to find terms of use source file %s", resourceName),e);
}
}
public String getTextForBrand(Brand brand) {
return termsOfUseText.get(brand);
}
}
Brand is an enum, and I need all the valid .txt files to be on the classpath. How do I make the IOException occur, given that the Brand enum contains all the valid brands and therfore all the .txt files for them exist?
Suggestions around refactoring the current code are welcome if it makes it more testable!
Three options I see right off:
Use PowerMock to mock IOUtils.toString(). I consider PowerMock to be quite a last resort. I'd rather refactor the source to something a little more test-friendly.
Extract the IOUtils call to a protected method. Create a test-specific subclass of your class that overrides this method and throws the IOException.
Extract the InputStream creation to a protected method. Create a test-specific subclass to override the method and return a mock InputStream.
I would suggest a bit of refactoring. All your methods are void, this usually means they are not functional.
For example, you can extract this functionality:
private String readTermsOfUseForBrand(InputStream termsOfUserIs) {
try {
String content = IOUtils.toString(in);
return content;
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to find terms of use source file %s", resourceName), e);
}
return null;
}
So that we can assert on the String result in our tests.
Of course this is not functional code, as it reads from an Input Stream. And it does so with IOUtils.toString() method that cannot be mocked easily (well, there's PowerMock but as Ryan Stewart said it's the last resort).
To test IO exceptions you can create a failing input stream (tested with JDK7):
public class FailingInputStream extends InputStream {
#Override
public int read() throws IOException {
throw new IOException("Test generated exception");
}
}
And test like that:
#Test
public void testReadTermsOfUseForBrand() {
FileLoader instance = new FileLoader();
String result = instance.readTermsOfUseForBrand(new FailingInputStream());
assertNull(result);
}
Missing file will cause NullPointerException because getResourceAsStream will return null and you will have in==null. IOException in this case may actually be pretty rare. If it's critical for you to see it, I can only think of instrumenting this code to throw it if code is executed in test scope. But is it really that important?
I would use a mock to accomplish this.
Example (untested, just to give you some thought):
#Test(expected=IllegalStateException.class)
public void testThrowIOException() {
PowerMockito.mockStatic(IOUtils.class);
PowerMockito.when(IOUtils.toString()).thenThrow(
new IOException("fake IOException"));
FileLoader fileLoader = new FileLoader();
Whitebox.invokeMethod(fileLoader,
"readAndStoreTermsOfUseForBrand", new Brand(...));
// If IllegalStateException is not thrown then this test case fails (see "expected" above)
}
Code below is completely untested
To cause the IOException use:
FileInputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
in.mark(0);
//read some data
in.reset(); //IOException
To test the IOException case use:
void test
{
boolean success = false;
try
{
//code to force ioException
}
catch(IOException ioex)
{
success = true;
}
assertTrue(success);
}
In JUnit4
#Test(expected=IOException.class)
void test
{
//code to force ioException
}
Other JUnit
void test
{
try
{
//code to force IOException
fail("If this gets hit IO did not occur, fail test");
}
catch(IOException ioex)
{
//success!
}
}
Hands up, I'm struggling with a programming question for M257 at OU, its formative and carries no marks and is due in a few days. I can't call the constructor from the test class and have struggled for several hours to no avail, the class compiles in Netbeans 6.91 fine but the constructor won't create the object. What am I doing wrong?
I had no problem with first question but am totally stuck here, obviously missing something significant - guidance please. The idea is to pass in the name of the file to the class, I can do the rest once I know the file is open and scanner initialised.
===============
/**
* Title: WordCounter class
* Description: M257 TMA01, Q2 - word counter class as described in instructions
* #author Andrew Broxholme
*/
package tma01q2;
import java.io.*;
import java.util.*;
public class WordCounter
{
//Class instance variables
public static int totalWords;
public static int totalEven;
public static int totalOdd;
public static int totalLetters;
private Scanner fileScanner;
String sourceFile;
String line; //The lines of the text file
//Single argument constructor, accepts source filename
public boolean WordCounter(String fileToRead)
{
sourceFile = fileToRead;
try
{
openRead();
while (fileScanner.hasNext())
{
// Process each line of the text file
line = fileScanner.nextLine();
System.out.println(line);
// countWords();
}
return true;
}
catch (Exception exp)
{
return false;
}
finally
{
fileScanner.close();
}
}
//openRead, opens the file and processes each line of the file until finished
private boolean openRead() throws IOException
{
try
{
fileScanner = new Scanner(sourceFile);
return true;
}
catch (Exception exp)
{
return false;
}
}
// More methods to be added
}
/*
* TestWordCounter.
* Description: Tests the WordCounter class as per TMA01q2 instructions
* #author Andrew Broxholme
* V1.0 30th April 2011
*/
package tma01q2;
public class TestWordCounter
{
//Create a WordCounter to process the specified text file.
public static void main(String[] args)
{
String testFile = "haiku.txt";
WordCounter fileStats = new WordCounter(testFile);
}
}
When I try to comiple this is what it passes back.
Compiling 1 source file to C:\M257\TMA01\TMA01Q2\build\classes
C:\M257\TMA01\TMA01Q2\src\tma01q2\TestWordCounter.java:18: cannot find symbol
symbol : constructor WordCounter(java.lang.String)
location: class tma01q2.WordCounter
WordCounter fileStats = new WordCounter(testFile);
1 error
C:\M257\TMA01\TMA01Q2\nbproject\build-impl.xml:246: The following error occurred while executing this line:
C:\M257\TMA01\TMA01Q2\nbproject\build-impl.xml:113: Compile failed; see the compiler error output for details.
I haven't given up on this and will update question if I find the answer first.
8th May 2011: The answers were helpful but in the end although in the end I gave up on this question as the further I got I realised I just didn't know enough about how subclasses inherit from superclasses and need to try some simpler (and to me more meaningful) examples to deepen my understanding. The problem though was that NetBeans is too good at suggesting what you need without telling you exactly why its doing what it is doing, fine if your an experienced java developer, but not so good if your starting out.
I'm already started (i.e read the brief) for TMA02 and will give myself a full two months, much more sensible one thinks!
This is not a constructor. Remove the boolean as return type - constructors don't have return types. So:
public WordCounter(String fileToRead)
instead of
public boolean WordCounter(String fileToRead)
And that's what the error tells you - the compiler cannot find a constructor with that name.
See: constructors
the signature of the constructor is wrong.
public WordCounter(String fileToRead)
{
sourceFile = fileToRead;
try
{
openRead();
while (fileScanner.hasNext())
{
// Process each line of the text file
line = fileScanner.nextLine();
System.out.println(line);
// countWords();
}
return true;
}
catch (Exception exp)
{
return false;
}
finally
{
fileScanner.close();
}
}
use constructor like this. Replace the signature of constructor to
public WordCounter(String fileToRead)