Some messages ask for the state of the object. Other messages ask an object to do something, For example, each BankAccount object was designed to have the related operations withdraw,
deposit, getBalance, and getID. These messages ask the two different BankAccount objects to return information:
anAccount.getID(); anAccount.getBalance(); anotherAccount.getID(); anotherAccount.getBalance();
These messages ask two different BankAccount objects to do something: anAccount.withdraw(40.00);
anAccount.deposit(100.00); anotherAccount.withdraw(20.00); anotherAccount.deposit(157.89);
The optional arguments—expressions between the parentheses—are the values required by the method to fulfill its responsibility. For example, withdraw needs to know how much money to withdraw. On the other hand, getBalance doesn’t need any arguments to return the current balance of the BankAccount object. The output below indicates deposit and withdraw messages modify the account balances in an expected manner:
// Construct two objects and send messages to them. public class ShowTwoBankAccountObjects {
public static void main(String[] args) {
BankAccount b1 = new BankAccount("Kim", 123.45); BankAccount b2 = new BankAccount("Chris", 500.00); System.out.println("Initial values");
System.out.println(b1.getID() + ": " + b1.getBalance()); System.out.println(b2.getID() + ": " + b2.getBalance()); b1.deposit(222.22);
b1.withdraw(20.00); b2.deposit(55.55); b2.withdraw(10.00); System.out.println();
System.out.println("Value after deposit and withdraw messages"); System.out.println(b1.getID() + ": " + b1.getBalance());
System.out.println(b2.getID() + ": " + b2.getBalance()); } }
Output
Initial values Kim: 123.45 Chris: 500.0Value after deposit and withdraw messages Kim: 325.67
3.2 Making Assertions about Objects with JUnit
The println statements in the program above reveal the changing state of objects. However, in such examples, many lines can separate the output from the messages that affect the objects. This makes it a bit awkward to match up the expected result with the code that caused the changes. The current and changing state of objects can be observed and confirmed by making assertions. An assertion is a statement that can relay the current state of an object or convey the result of a message to an object. Assertions can be made with methods such assertEquals.
General Form: JUnit's assertEquals method for int and double values
assertEquals(int expected, int actual);
assertEquals(double expected, double actual, double errorTolerance); Examples to assert integer expressions:
assertEquals(2, 5 / 2);
assertEquals(14, 39 % 25);
Examples to assert a floating point expression:
assertEquals(325.67, b1.getBalance(), 0.001);
assertEquals(545.55, b2.getBalance(), 0.001);
With assertEquals, an assertion will be true—or will "pass"—if the expected value equals the
actual value. When comparing floating-point values, a third argument is needed to represent the error tolerance, which is the amount by which two real numbers may differ and still be equal. (Due to round off error, and the fact that numbers are stored in base 2 (binary) rather than in base 10 (decimal), two expressions that we consider “equal” may actually differ by a very small amount. This textbook will often use the very small error tolerance of 1e-14 or
0.00000000000001. This means that the following two numbers would be considered equal within 1e-14:
assertEquals(1.23456789012345, 1.23456789012346, 1e-14);
In contrast, these numbers are not considered equal when using an error factor of 1e-14.
assertEquals(1.23456789012345, 1.23456789012347, 1e-14);
So using 1e14 ensures two values are equals to 13 decimal places, which is about as close as you can get. JUnit assertions allow us to place the expected value next to messages that reveal the actual state. This makes it easer to demonstrate the behavior of objects and to learn about new types. Later, you will see how assertions help in designing and testing your own Java classes, by making sure they have the correct behavior.
The assertEquals method is in the Assert class of org.junit. The Assert class needs to be
imported (shown later) or assertEquals needs to be qualified (shown next). // Construct two BankAccount objects
BankAccount anAccount = new BankAccount("Kim", 0.00);
BankAccount anotherAccount = new BankAccount("Chris", 500.00); // These assertions pass
org.junit.Assert.assertEquals(0.00, anAccount.getBalance(), 1e-14); org.junit.Assert.assertEquals("Kim", anAccount.getID());
org.junit.Assert.assertEquals("Chris", anotherAccount.getID());
org.junit.Assert.assertEquals(500.00, anotherAccount.getBalance(), 1e-14); // Send messages to the BankAccount objects
anAccount.deposit(222.22); anAccount.withdraw(20.00);
anotherAccount.deposit(55.55); anotherAccount.withdraw(10.00); // These assertions pass
org.junit.Assert.assertEquals(202.22, anAccount.getBalance(), 1e-14); org.junit.Assert.assertEquals(545.55, anotherAccount.getBalance(), 1e-14);
To make these assertions, you must have access to the JUnit testing framework, which is
available in virtually all Java development environments. Eclipse does. Then assertions like those above can be placed in methods preceded by @Test. These methods are known as test methods.
They are most often used to test a new method. The test methods here demonstrate some new types. A test method begins in a very specific manner:
@org.junit.Test
public void testSomething() { // more to come
Much like the main method, test methods are called from another program (JUnit). Test methods need things from the org.junit packages. This code uses fully qualified names.
public class FirstTest {
@org.junit.Test // Marks this as a test method.
public void testDeposit() {
BankAccount anAccount = new BankAccount("Kim", 0.00); anAccount.deposit(123.45);
org.junit.Assert.assertEquals(123.45, anAccount.getBalance(), 0.01); }
}
Adding imports shortens code in all test methods. This feature allows programmers to write the method name without the class to which the method belongs. The modified class shows that imports reduce the amount of code by org.junit.Assert and ord.junit for every test method
and assertion, which is a good thing since much other code that is required.
import static org.junit.Assert.assertEquals;
import org.junit.Test ;
public class FirstTest {
@Test // Marks this as a test method.
public void testDeposit() {
BankAccount anAccount = new BankAccount("Kim", 0.00); anAccount.deposit(123.45);
assertEquals(123.45, anAccount.getBalance());
}
@Test // Marks this as a test method.
public void testWithdraw() {
BankAccount anotherAccount = new BankAccount("Chris", 500.00); anotherAccount.withdraw(160.01);
assertEquals(339.99, anotherAccount.getBalance());
}
} // End unit test for BankAccount
Running JUnit
An assertion passes when the actual value equals the expected value in assertEquals.
assertEquals(4, 9 / 2); // Assertion passes
assertEquals(4.5, 9 / 2, 1e-14); // Assertion fails
With integrated development environments such as Eclipse, Netbeans, Dr. Java, BlueJ, when an assertion fails, you see a red bar. For example, this screenshot of Eclipse shows a red bar.
The expected and actual values are shown in the lower left corner when the code in FirstTest.java is run as a JUnit test. Changing the testDeposit method to have the correct expected value results in a green bar, indicating all assertions have passed successfully. Here is JUnit's window when all assertions pass: