One of the key components of writing good software is to ensure that our code is tested. Clearly we can test our applications by running them and checking that we receive the expected response, but in anything but the simplest of apps, some errors can be hard to track - particularly if they do not cause the application to crash. Consider the following code
package uk.ac.chester;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner inputScanner = new Scanner(System.in);
System.out.println("Please enter a phrase:");
String line = inputScanner.nextLine();
char letter = line.charAt(0);
char upperLetter = convertToUpperCase(letter);
System.out.println(upperLetter);
}
public static char convertToUpperCase(char letter) {
String letterAsString = String.valueOf(letter);
letterAsString = letterAsString.toUpperCase();
char upperLetter = letterAsString.charAt(0);
return upperLetter;
}
}
In order to test the convertToUpperCase()
method we are required to write correct code to use the Scanner, get a string, get a character in a string and then print the output. Even if we wrote code to create the char and modified it to test multiple letters it is still an unwieldy way to test things. Consider if it was part of a large programme, where we couldn't just modify the programmes core functionality to test the one method, it would be very hard to check that the method works correctly.
A solution to this problem is to write code which has the sole purpose of testing a method. The next steps walk you through the process of creating a unit test for the convertToUpperCase method
convertToUpperCase()
methodpackage uk.ac.chester;
import static org.junit.Assert.*;
/**
* Created by Andrew on dd/mm/yyyy.
*/
public class MainTest {
@org.junit.Test
public void convertToUpperCase() throws Exception {
}
}
The word Assert on the top line will be red, to resolve this error, click on it, press Alt+Enter and choose the 'Add library 'JUnit4' to classpath option, as shown here:
The method that is created is designated as a testing method by the @Test
code, the additional syntax that you may not have seen before: throws Exception
means that the code can be expected to error (which, if we write our tests correctly) it would if there is an error in the convertToUpperCase()
method in the Main class.
You may also notice that the method is inside a new class called MainTest
this convention indicates that methods here will test the corresponding methods in the Main class
package uk.ac.chester;
import org.junit.Assert;
/**
* Created by Andrew on dd/mm/yyyy.
*/
public class MainTest {
@org.junit.Test
public void convertToUpperCase() throws Exception {
char resultWithA = Main.convertToUpperCase('a');
Assert.assertEquals("a must convert to A", 'A', resultWithA);
}
}
The three changes made are as follows:
convertToUpperCase()
method in the Main class with the lowercase letter 'a' as the parameter and stores the result as a char public static char convertToUpperCase(char letter) {
// String letterAsString = String.valueOf(letter);
// letterAsString = letterAsString.toUpperCase();
// char upperLetter = letterAsString.charAt(0);
// return upperLetter;
return 'A';
}
char resultWithF = Main.convertToUpperCase('f');
Assert.assertEquals("f must convert to F", 'F', resultWithF);
char resultWithUpperC = Main.convertToUpperCase('C');
Assert.assertEquals("C must be unchanged", 'C', resultWithUpperC);
char resultWith4 = Main.convertToUpperCase('4');
Assert.assertEquals("4 must be unchanged", '4', resultWith4);
char resultWithUmlautU = Main.convertToUpperCase('ü');
Assert.assertEquals("ü must convert to Ü", 'Ü', resultWithUmlautU);
The process of writing tests requires a judgement call to be made my the developer. What are the likely errors to guard against in any given method, and how much time is required to write the tests. At this stage, writing tests may take a long time, for little perceivable gain, however as you become a more proficient programmer, you will become faster at writing tests, and more able to discern which tests should be written.
A further benefit of writing unit tests is that if you make a change to a method (for example you refactor it), it is very quick to re-run the tests to check you haven't broken it!
At the top right hand corner of IntelliJ IDEA is a dropdown list - this shows both the test application and the main application - use the drop down to switch between them so the 'play' button will run the desired application. Ignore the edit and save options for now.
public static String makeTitleCase(String phrase) {
String result = "";
boolean startOfWord = true;
for (int i = 0; i < phrase.length(); i++) {
char letter = phrase.charAt(i);
if (letter == ' ') {
startOfWord = true;
} else {
if (startOfWord) {
letter = convertToUpperCase(letter);
startOfWord = false;
}
}
result += letter;
}
return result;
}
makeTitleCase
method (with that text) and finally print the result to the screen. Verify that typing 'hello world' (without the quotes) results in 'Hello World' being printed.Revisit previous tasks you have completed. Write unit tests to verify the methods in them. Where all the code is in the main method, extract methods as appropriate before writing the unit tests.