0% found this document useful (0 votes)
13 views46 pages

3-OOP in Java - Version 1.0 - 2016-12-01

Uploaded by

khuubaolong2k3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views46 pages

3-OOP in Java - Version 1.0 - 2016-12-01

Uploaded by

khuubaolong2k3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 46

Principles of Software Construction

(NGUYÊN LÝ XÂY DỰNG PHẦN MỀM)

Software Engineering Dept., CICT,


Cantho Unversity
Lecturer: Trương Minh Thái
email: [email protected]
Phần dành cho đơn vị

Contents

• Introduction to Java
• Object-Oriented Programming in Java
• Specification and Testing

1
Contents

• Introduction to Java
• Object-Oriented Programming in Java
– Object-oriented programming basics
– Information hiding
– Exceptions
• Specification and Testing

Contents

• Introduction to Java
• Object-Oriented Programming in Java
– Object-oriented programming basics
– Information hiding
– Exceptions
• Specification and Testing

2
Objects

• An object is a bundle of state and behavior


• State – the data contained in the object
– In Java, these are the fields of the object
• Behavior – the actions supported by the object
– In Java, these are called methods
– Method is just OO-speak for function
– Invoke a method = call a function

Classes

• Every object has a class


– A class defines methods and fields
– Methods and fields collectively known as members
• Class defines both type and implementation
– type ≈ where the object can be used
– implementation ≈ how the object does things
• Loosely speaking, the methods of a class are its
Application Programming Interface (API)
– Defines how users interact with instances

3
Class example – complex numbers
class Complex {
private double re; // Real Part
private double im; // Imaginary Part
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public double realPart() { return re; }
public double imaginaryPart() { return im; }
public double r() { return Math.sqrt(re * re + im * im); }
public double theta() { return Math.atan(im / re); }
public Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im);
}
public Complex minus(Complex c) { ... }
public Complex times(Complex c) { ... }
public Complex divideby(Complex c) { ... }
}

Class usage example

public class ComplexUser {


public static void main(String args[]) {
Complex c = new Complex(-1, 0);
Complex d = new Complex(0, 1);
Complex e = c.plus(d);
System.out.println(e.realPart() + " + "+ e.imaginaryPart() + "i");
e = c.times(d);
System.out.println(e.realPart() + " + "+ e.imaginaryPart() + "i");
}
}
• When you run this program, it prints
-1.0 + 1.0i
-0.0 + -1.0i

4
Interfaces and implementations

• Multiple implementations of API can coexist


– Multiple classes can implement the same API
– They can differ in performance and behavior
• In Java, an API is specified by interface or
class
– Interface provides only an API
– Class provides an API and an implementation
– A Class can implement multiple interfaces

An interface to go with our class

public interface Complex {


// No constructors, fields, or implementations!
double realPart();
double imaginaryPart();
double r();
double theta();
Complex plus(Complex c);
Complex minus(Complex c);
Complex times(Complex c);
Complex dividedBy(Complex c);
}
• An interface defines but does not implement API
• Note: Read more interface in Java and interface vs class

5
Modifying class to use interface
class OrdinaryComplex implements Complex {
double re; // Real Part
double im; // Imaginary Part
public OrdinaryComplex(double re, double im) {
this.re = re;
this.im = im;
}
public double realPart() { return re; }
public double imaginaryPart() { return im; }
public double r() { return Math.sqrt(re * re + im * im); }
public double theta() { return Math.atan(im / re); }
public Complex plus(Complex c) {
return new OrdinaryComplex(re + c.realPart(), im + c.imaginaryPart());
}
public Complex minus(Complex c) { ... }
public Complex times(Complex c) { ... }
public Complex dividedby(Complex c) { ... }
}by

Modifying client to use interface

public class ComplexUser {


public static void main(String args[]) {
Complex c = new OrdinaryComplex(-1, 0);
Complex d = new OrdinaryComplex(0, 1);
Complex e = c.plus(d);
System.out.println(e.realPart() + " + "+ e.imaginaryPart() + "i");
e = c.times(d);
System.out.println(e.realPart() + " + "+ e.imaginaryPart() + "i");
}
}
When you run this program, it still prints
-1.0 + 1.0i
-0.0 + -1.0i

6
Interface permits multiple implementations
class PolarComplex implements Complex {
double r;
double theta;
public PolarComplex(double r, double theta) {
this.r = r;
this.theta = theta;
}
public double realPart() { return r * Math.cos(theta) ; }
public double imaginaryPart() { return r * Math.sin(theta) ; }
public double r() { return r; }
public double theta() { return theta; }
public Complex plus(Complex c) { ... } // Completely different impls
public Complex minus(Complex c) { ... }
public Complex times(Complex c) { ... }
public Complex dividedBy(Complex c) { ... }
}

Interface decouples client from implementation

public class ComplexUser {


public static void main(String args[]) {
Complex c = new PolarComplex(Math.PI, 1); // -1
Complex d = new PolarComplex(Math.PI/2, 1); // i
Complex e = c.plus(d);
System.out.println(e.realPart() + " + " + e.imaginaryPart() + "i");
e = c.times(d);
System.out.println(e.realPart() + " + " + e.imaginaryPart() + "i");
}
}

• When you run this program, it STILL prints


-1.0 + 1.0i
-0.0 + -1.0i

7
Why multiple implementations?

• Different performance
– Choose implementation that works best for your use
• Different behavior
– Choose implementation that does what you want
– Behavior must comply with interface spec (“contract”)
• Often performance and behavior both vary
– Provides a functionality – performance tradeoff
– Example: HashSet, TreeSet

Java interfaces and classes

• Organize program objects types


– Each type offers a specific set of operations
– Objects are otherwise opaque
• Interfaces vs. classes
– Interface: specifies expectations
– Class: delivers on expectations (the implementation)

8
Contents

• Introduction to Java
• Object-Oriented Programming in Java
– Object-oriented programming basics
– Information hiding
– Exceptions
• Specification and Testing

Information hiding

• Single most important factor that distinguishes a well-


designed module from a bad one is the degree to which
it hides internal data and other implementation details
from other modules
• Well-designed code hides all implementation details
– Cleanly separates API from implementation
– Modules communicate only through APIs
– The are oblivious to each others’ inner workings
• Known as information hiding or encapsulation
• Fundamental tenet of software design [Parnas, ‘72]

9
Benefits of information hiding

• Decouples the classes that comprise a system


– Allows them to be developed, tested, optimized, used,
understood, and modified in isolation
• Speeds up system development
– Classes can be developed in parallel
• Eases burden of maintenance
– Classes can be understood more quickly and debugged with
little fear of harming other modules
• Enables effective performance tuning
– “Hot” classes can be optimized in isolation
• Increases software reuse
– Loosely-coupled classes often prove useful in other contexts

Information hiding with interfaces

• Declare variables using interface type


• Client can use only interface methods
• Fields not accessible from client code
• But this only takes us so far
– Client can access non-interface members directly
– In essence, it’s voluntary information hiding

10
Mandatory Information hiding
visibility modifiers for members

• private – Accessible only from declaring class


• package-private – Accessible from any class
in the package where it is declared
– Technically known as default access
– You get this if no access modifier is specified
• protected – Accessible from subclasses of
declaring class (and within package)
• public – Accessible from anywhere

Controlling Access to Members of a Class

• Access level modifiers determine whether other classes can use a


particular field or invoke a particular method. There are two levels of
access control:
• At the top level—public, or package-private (no explicit
modifier).
• At the member level—public, private, protected, or package-
private (no explicit modifier).

11
Controlling Access to Members of a Class

The following table shows where the members of the Alpha class are
visible for each of the access modifiers that can be applied to them

Hiding interior state in OrdinaryComplex

class OrdinaryComplex implements Complex {


private double re; // Real Part
private double im; // Imaginary Part
public OrdinaryComplex(double re, double im) {
this.re = re;
this.im = im;
}
public double realPart() { return re; }
public double imaginaryPart() { return im; }
public double r() { return Math.sqrt(re * re + im * im); }
public double theta() { return Math.atan(im / re); }
public Complex add(Complex c) {
return new OrdinaryComplex(re + c.realPart(), im + c.imaginaryPart());
}
public Complex subtract(Complex c) { ... }
public Complex multiply(Complex c) { ... }
public Complex divide(Complex c) { ... }
}

12
Discussion

• You know the benefits of private fields


• What are the benefits of private methods?

Best practices for information hiding

• Carefully design your API


• Provide only functionality required by clients
– All other members should be private
• You can always make a private member public
later without breaking clients
– But not vice-versa!

13
Contents

• Introduction to Java
• Object-Oriented Programming in Java
– Object-oriented programming basics
– Information hiding
– Exceptions
• Specification and Testing

What does this code do?


FileInputStream fIn = new FileInputStream(fileName);
if (fIn == null) {
switch (errno) {
case _ENOFILE:
System.err.println(“File not found: “ + …);
return -1;
default:
System.err.println(“Something else bad happened: “ + …);
return -1;
}
}
DataInput dataInput = new DataInputStream(fIn);
if (dataInput == null) {
System.err.println(“Unknown internal error.”);
return -1; // errno > 0 set by new DataInputStream
}
int i = dataInput.readInt();
if (errno > 0) {
System.err.println(“Error reading binary data from file”);
return -1;
}
return i;

14
Compare to:

FileInputStream fileInput = null;


try {
fileInput = new FileInputStream(fileName);
DataInput dataInput = new DataInputStream(fileInput);
return dataInput.readInt();
} catch (FileNotFoundException e) {
System.out.println("Could not open file " + fileName);
} catch (IOException e) {
System.out.println("Couldn’t read file: " + e);
} finally {
if (fileInput != null) fileInput.close();
}

Exceptions

• Notify the caller of an exceptional condition by


automatic transfer of control
• Semantics:
– Propagates up stack until main method is
reached (terminates program), or exception is
caught
• Sources:
– Program – e.g., IllegalArgumentException
– JVM – e.g., StackOverflowError

15
Control-flow of exceptions

public static void test() {


try {
System.out.println("Top");
int[] a = new int[10];
a[42] = 42;
System.out.println("Bottom");
} catch (NegativeArraySizeException e) {
System.out.println("Caught negative array size");
}
}
public static void main(String[] args) {
try {
test();
} catch (IndexOutOfBoundsException e) {
System.out.println("Caught index out of bounds");
}
}

Checked vs. unchecked exceptions

• Checked exception
– Exception which are checked at Compile time called Checked
Exception
– Represent invalid conditions in areas outside the immediate
control of the program (invalid user input, database problems,
network outages, absent files)
– Must be caught or propagated, or program won’t compile
• Unchecked exception
– Unchecked Exception in Java is those Exceptions whose
handling is NOT verified during Compile time
– Represent defects in the program (bugs) - often invalid
arguments passed to a non-private method.
– Unchecked runtime exceptions represent conditions that,
generally speaking, reflect errors in your program's logic and
cannot be reasonably recovered from at run time

16
The exception hierarchy in Java

17
Design choice: checked and unchecked
exceptions and return values

• Unchecked exception
– Programming error, other unrecoverable failure
• Checked exception
– An error that every caller should be aware of and
handle
• Special return value (e.g., null from Map.get)
– Common but atypical result
• Do NOT use return codes
• NEVER return null to indicate a zero-length result
– Use a zero-length list or array instead

Creating and throwing your own


exceptions
public class SpanishInquisitionException extends RuntimeException {
public SpanishInquisitionException() {
}
}
public class HolyGrail {
public void seek() {
...
if (heresyByWord() || heresyByDeed())
throw new SpanishInquisitionException();
...
}
}

18
Benefits of exceptions

• You can’t forget to handle common failure modes


– Compare: using a flag or special return value
• Provide high-level summary of error, and stack trace
– Compare: core dump in C
• Improve code structure
– Separate normal code path from exceptional
– Ease task of recovering from failure
• Ease task of writing robust, maintainable code

• Read more at
https://docs.oracle.com/javase/tutorial/essential/exceptio
ns/index.html

Guidelines for using exceptions (1)

• Avoid unnecessary checked exceptions (EJ Item 59)


• Favor standard exceptions (EJ Item 60)
– IllegalArgumentException – invalid parameter value
– IllegalStateException – invalid object state
– NullPointerException – null paramwhere prohibited
– IndexOutOfBoundsException – invalid index param
• Throw exceptions appropriate to abstraction (EJ Item 61)

19
Guidelines for using exceptions (2)

• Document all exceptions thrown by each method


– Checked and unchecked (EJ Item 62)
– But don’t declare unchecked exceptions!
• Include failure-capture info in detail message (Item 63)
– throw new IlegalArgumentException( "Modulus must be
prime: " + modulus);
• Don’t ignore exceptions (EJ Item 65)
// Empty catch block IGNORES exception – Bad smell in
code!
try {
...
} catch (SomeException e) { }

Remember this slide?


You can do much better!
FileInputStream fileInput = null;
try {
FileInputStream fileInput = new FileInputStream(fileName);
DataInput dataInput = new DataInputStream(fileInput);
return dataInput.readInt();
} catch (FileNotFoundException e) {
System.out.println("Could not open file " + fileName);
} catch (IOException e) {
System.out.println("Couldn’t read file: " + e);
} finally {
if (fileInput != null) fileInput.close();
}

20
The solution: try-with-resources
Automatically closes resources
try (DataInput dataInput =
new DataInputStream(new FileInputStream(fileName))) {
return dataInput.readInt();
} catch (FileNotFoundException e) {
System.out.println("Could not open file " + fileName);
} catch (IOException e) {
System.out.println("Couldn’t read file: " + e);
}

File copy without ARM (Automatic


Resource Management)
static void copy(String src, String dest) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dest);
try {
byte[] buf = new byte[8 * 1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}

21
File copy with ARM

static void copy(String src, String dest) throws IOException


{
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
byte[] buf = new byte[8 * 1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}

Contents

• Introduction to Java
• Object-Oriented Programming in Java
• Specification and Testing
– Specifying program behavior – contracts
– Testing correctness – Junit and friends

22
Contents

• Introduction to Java
• Object-Oriented Programming in Java
• Specification and Testing
– Specifying program behavior – contracts
– Testing correctness – Junit and friends

What is a contract?

• Agreement between an object and its user


• Includes
– Method signature (type specifications)
– Functionality and correctness expectations
– Performance expectations
• What the method does, not how it does it
– Interface (API), not implementation

23
Method contract details

• States method’s and caller’s responsibilities


• Analogy: legal contract
– If you pay me this amount on this schedule…
– I will build a with the following detailed specification
– Some contracts have remedies for nonperformance
• Method contract structure
– Preconditions: what method requires for correct operation
– Postconditions: what method establishes on completion
– Exceptional behavior: what it does if precondition violated
• Defines what it means for implementation to be correct

Formal contract specification


Java Modelling Language (JML)
/*@ requires len >= 0 && array != null && array.length == len;
@
@ ensures \result == (\sum int j; 0 <= j && j < len; array[j]);
@*/
int total(int array[], int len);

Theoretical approach
• Advantages
– Runtime checks generated automatically
– Basis for formal verification
– Automatic analysis tools
• Disadvantages
– Requires a lot of work
– Impractical in the large
– Some aspects of behavior not amenable to formal specification

24
Textual specification - Javadoc

• Practical approach
• Document
– Every parameter
– Return value
– Every exception (checked and unchecked)
– What the method does, including
• Purpose
• Side effects
• Any thread safety issues
• Any performance issues
• Do not document implementation details

Specifications in the real world


Javadoc

25
Contents

• Introduction to Java
• Object-Oriented Programming in Java
• Specification and Testing
– Specifying program behavior – contracts
– Testing correctness – Junit and friends

Semantic correctness
adherence to contracts

• Compiler ensures types are correct (type-


checking)
– Prevents many runtime errors, such as “Method
Not Found” and “Cannot add boolean to int”
• Static analysis tools (e.g., FindBugs) recognize
many common problems (bug patterns)
– Overiding equals without overriding hashCode
• But how do you ensure semantic correctness?

26
Formal verification

• Use mathematical methods to prove correctness


with respect to the formal specification
• Formally prove that all possible executions of an
implementation fulfill the specification
• Manual effort; partial automation; not automatically
decidable (decision problem)
"Testing shows the presence,
not the absence of bugs.”
Edsger W. Dijkstra, 1969

Testing

• Executing the program with selected inputs in


a controlled environment
• Goals
– Reveal bugs, so they can be fixed (main goal)
– Assess quality
– Clarify the specification, documentation
“Beware of bugs in the above code; I
have only proved it correct, not tried it.”
Donald Knuth, 1977

27
Manual testing?

Automate testing

• Execute a program with specific inputs, check


output for expected values
• Set up testing infrastructure
• Execute tests regularly
– After every change

28
Unit tests

• Unit tests for small units: methods, classes,


subsystems
– Smallest testable part of a system
– Test parts before assembling them
– Intended to catch local bugs
• Typically written by developers
• Many small, fast-running, independent tests
• Few dependencies on other system parts or
environment
• Insufficient, but a good starting point

JUnit

• Popular unit-testing framework for Java


• Easy to use
• Tool support available
• Can be used as design mechanism

29
Selecting test cases: common strategies

• Read specification
• Write tests for
– Representative case
– Invalid cases
– Boundary conditions
• Write stress tests
– Automatically generate huge numbers of test cases
• Think like an attacker
– The tester’s goal is to find bugs!
• How many test should you write?
– Aim to cover the specification
– Work within time/money constraints

JUnit conventions

• TestCase collects multiple tests (in one class)


• TestSuite collects test cases (typically
package)
• Tests should run fast
• Tests should be independent

30
Testable code

• Think about testing when writing code


• Unit testing encourages you to write testable code
• Modularity and testability go hand in hand
• Same test can be used on multiple implementations
of an interface!
• Test-Driven Development
– A design and development method in which you
write tests before you write the code
– Writing tests can expose API weaknesses!

Run tests frequently

• You should only commit code that is passing all


tests
• Run tests before every commit
• If entire test suite becomes too large and slow for
rapid feedback:
– Run local package-level tests ("smoke tests“)
frequently
– Run all tests nightly
– Medium sized projects easily have 1000s of test
cases
• Continuous integration servers help to scale testing

31
Home work

• Reading Junit test


• Write test cases by using Junit

JUnit

32
Test suites

• Obviously you have to test your code to get it working in the first
place
– You can do ad hoc testing (testing whatever occurs to you at the
moment), or
– You can build a test suite (a thorough set of tests that can be run at
any time)
• Disadvantages of writing a test suite
– It’s a lot of extra programming
• True—but use of a good test framework can help quite a bit
– You don’t have time to do all that extra work
• False—Experiments repeatedly show that test suites reduce debugging
time more than the amount spent building the test suite
• Advantages of having a test suite
– Your program will have many fewer bugs
– It will be a lot easier to maintain and modify your program
68 • This is a huge win for programs that, unlike class assignments, get actual
use!

Example: Old way vs. new way


• int max(int a, int b) {
if (a > b) { • @Test
return a; void testMax() {
} else { assertEquals(7, max(3, 7));
return b;
assertEquals(3, max(3, -7));
}
} }
• void testMax() {
int x = max(3, 7);
if (x != 7) {
System.out.println("max(3, 7) gives " + x);
}
x = max(3, -7);
if (x != 3) {
System.out.println("max(3, -7) gives " + x);
}
}
• public static void main(String[] args) {
new MyClass().testMax();
69}

33
XP approach to testing
• In the Extreme Programming approach,
– Tests are written before the code itself
– If code has no automated test case, it is assumed not to work
– A test framework is used so that automated testing can be done
after every small change to the code
• This may be as often as every 5 or 10 minutes
– If a bug is found after development, a test is created to keep the
bug from coming back
• Consequences
– Fewer bugs
– More maintainable code
– Continuous integration—During development, the program always
works—it may not do everything required, but what it does, it does
right
70

JUnit

• JUnit is a framework for writing tests


– JUnit was written by Erich Gamma (of Design
Patterns fame) and Kent Beck (creator of XP
methodology)
– JUnit uses Java’s reflection capabilities (Java
programs can examine their own code)
– JUnit helps the programmer:
• define and execute tests and test suites
• formalize requirements and clarify architecture
• write and debug code
• integrate code and always be ready to release a working
version
– JUnit is not included in Sun’s SDK, but almost all
71
IDEs include it

34
Terminology
• A test fixture sets up the data (both objects and primitives)
that are needed to run tests
– Example: If you are testing code that updates an employee record,
you need an employee record to test it on
• A unit test is a test of a single class
• A test case tests the response of a single method to a
particular set of inputs
• A test suite is a collection of test cases
• A test runner is software that runs tests and reports results

• An integration test is a test of how well classes work


together
– JUnit provides some limited support for integration tests

72

Once more, in pictures

test suite test runner


another unit test
test case (for one method) • A unit test tests the methods
another test case in a single class
• A test case tests (insofar as
another unit test possible) a single method
another test case – You can have multiple test
another test case cases for a single method
another test case • A test suite combines unit
tests
unit test (for one class)
• The test fixture provides
software support for all this
test case (for one method)
• The test runner runs unit tests
another test case
or an entire test suite
• Integration testing (testing that
test fixture
73 it all works together) is not
well supported by JUnit

35
Writing a JUnit test class, I
– Start by importing these JUnit 4 classes:
• import org.junit.*;
import static org.junit.Assert.*; // note static import

– Declare your test class in the usual way


• public class MyProgramTest {

– Declare an instance of the class being tested


– You can declare other variables, but don’t give
them initial values here
• public class MyProgramTest {
MyProgram program;
74
int someVariable;

Writing a JUnit test class, II2


– Define a method (or several methods) to be executed before each
test
– Initialize your variables in this method, so that each test starts with
a fresh set of values
• @Before
public void setUp() {
program = new MyProgram();
someVariable = 1000;
}

– You can define one or more methods to be executed after each


test
– Typically such methods release resources, such as files
– Usually there is no need to bother with this method
• @After
75
public void tearDown() {
}

36
A simple example
• Suppose you have a class Arithmetic with methods int multiply(int x, int y), and
boolean isPositive(int x)

• import org.junit.*;
import static org.junit.Assert.*;

• public class ArithmeticTest {

 @Test
public void testMultiply() {
assertEquals(4, Arithmetic.multiply(2, 2));
assertEquals(-15, Arithmetic.multiply(3, -5));
}

 @Test
public void testIsPositive() {
assertTrue(Arithmetic.isPositive(5));
assertFalse(Arithmetic.isPositive(-5));
assertFalse(Arithmetic.isPositive(0));
}

76}

Assert methods I
• Within a test,
– Call the method being tested and get the actual result
– Assert what the correct result should be with one of the assert methods
– These steps can be repeated as many times as necessary

• An assert method is a JUnit method that performs a test, and throws


an AssertionError if the test fails
– JUnit catches these Errors and shows you the result

• static void assertTrue(boolean test)


static void assertTrue(String message, boolean test)
– Throws an AssertionErrorif the test fails
– The optional message is included in the Error

• static void assertFalse(boolean test)


static void assertFalse(String message, boolean test)
– Throws an AssertionErrorif the test fails
77

37
Example: Counter class
• For the sake of example, we will create and test a trivial
“counter” class
– The constructor will create a counter and set it to zero
– The increment method will add one to the counter and return the
new value
– The decrement method will subtract one from the counter and
return the new value
• We write the test methods before we write the code
– This has the advantages described earlier
– However, we usually write the method stubs first, and let the IDE
generate the test method stubs
• Don’t be alarmed if, in this simple example, the JUnit tests
are more code than the class itself

78

JUnit tests for Counter


public class CounterTest {
Counter counter1; // declare a Counter here

@Before
void setUp() {
counter1 = new Counter(); // initialize the Counter here
}

@Test • Note that each test


public void testIncrement() { begins with a brand
assertTrue(counter1.increment() == 1); new counter
assertTrue(counter1.increment() == 2);
} • This means you don’t
have to worry about
@Test the order in which the
public void testDecrement() { tests are run
assertTrue(counter1.decrement() == -1);
}
}
79

38
The Counter class itself

 public class Counter { • Is JUnit testing overkill for this


int count = 0; little class?
• The Extreme Programming
public int increment() { view is: If it isn’t tested, it
return count += 1; doesn’t work
} • You are not likely to have
many classes this trivial in a
public int decrement() { real program, so writing JUnit
tests for those few trivial
return count -= 1; classes is no big deal
}
• Often even XP programmers
don’t bother writing tests for
public int getCount() { simple getter methods such as
return count; getCount()
} • We only used assertTrue in
} this example, but there are
80 additional assert methods

Warning: equals
• You can compare primitives with ==
• Java has a method x.equals(y), for comparing objects
– This method works great for Strings and a few other Java classes
– For objects of classes that you create, you have to define equals
• assertEquals(expected, actual) uses == or equals
• To define equals for your own objects, define exactly this
method:
public boolean equals(Object obj) { ... }
– The argument must be of type Object, which isn’t what you want,
so you must cast it to the correct type (say, Person):
– public boolean equals(Object something) {
Person p = (Person)something;
return this.name == p.name; // test whatever you like here
}
• 81We’ll talk much more about equals later

39
Assert methods II
• assertEquals(expected, actual)
assertEquals(String message, expected, actual)
– expected and actual must be both objects or the same primitive
type
– For objects, uses your equals method, if you have defined it
properly, as described on the previous slide

• assertSame(Object expected, Object actual)


assertSame(String message, Object expected, Object actual)
– Asserts that two arguments refer to the same object

• assertNotSame(Object expected, Object actual)


assertNotSame(String message, Object expected, Object actual)
– Asserts that two objects do not refer to the same object

82

Assert methods III


• assertNull(Object object)
assertNull(String message, Object object)
– Asserts that the object is null (undefined)

• assertNotNull(Object object)
assertNotNull(String message, Object object)
– Asserts that the object is null

• fail()
fail(String message)
– Causes the test to fail and throw an AssertionFailedError
– Useful as a result of a complex test, when the other assert
methods aren’t quite what you want

83

40
Writing a JUnit test class, III

• This page is really only for expensive setup, such as when you
need to connect to a database to do your testing

– If you wish, you can declare one method to be executed just once, when
the class is first loaded
• @BeforeClass
public static void setUpClass() throws Exception {
// one-time initialization code
}

– If you wish, you can declare one method to be executed just once, to do
cleanup after all the tests have been completed
• @AfterClass
public static void tearDownClass() throws Exception {
// one-time cleanup code
}
84

Special features of @Test

– You can limit how long a method is allowed to take


– This is good protection against infinite loops
– The time limit is specified in milliseconds
– The test fails if the method takes too long
• @Test (timeout=10)
public void greatBig() {
assertTrue(program.ackerman(5, 5) > 10e12);
}

– Some method calls should throw an exception


– You can specify that a particular exception is expected
– The test will pass if the expected exception is thrown, and fail otherwise
• @Test (expected=IllegalArgumentException.class)
public void factorial() {
program.factorial(-5);
}
85

41
Test-Driven Development (TDD)
• It is difficult to add JUnit tests to an existing program
– The program probably wasn’t written with testing in mind
• It’s actually better to write the tests before writing the code
you want to test
• This seems backward, but it really does work better:
– When tests are written first, you have a clearer idea what to do
when you write the methods
– Because the tests are written first, the methods are necessarily
written to be testable
– Writing tests first encourages you to write simpler, single-purpose
methods
– Because the methods will be called from more than one
environment (the “real” one, plus your test class), they tend to be
more independent of the environment

86

Stubs

• In order to run our tests, the methods we are testing


have to exist, but they don’t have to be right
• Instead of starting with “real” code, we start with stubs—
minimal methods that always return the same values
– A stub that returns void can be written with an empty body
– A stub that returns a number can return 0 or -1 or 666, or
whatever number is most likely to be wrong
– A stub that returns a boolean value should usually return false
– A stub that returns an object of any kind (including a String or an
array) should return null
• When we run our test methods with these stubs, we
want the test methods to fail!
– This helps “test the tests”—to help make sure that an incorrect
method doesn’t pass the tests
87

42
Ignoring a test

– The @Ignore annotation says to not run a test


• @Ignore("I don’t want Dave to know this doesn’t work")
@Test
public void add() {
assertEquals(4, program.sum(2, 2));
}
– You shouldn’t use @Ignore without a very good reason!

88

Test suites

– You can define a suite of tests


• @RunWith(value=Suite.class)
@SuiteClasses(value={
MyProgramTest.class,
AnotherTest.class,
YetAnotherTest.class
})
public class AllTests { }

89

43
JUnit in Eclipse
• If you write your method stubs first (as on the previous slide), Eclipse
will generate test method stubs for you
• To add JUnit 4 to your project:
– Select a class in Eclipse
– Go to File  New...  JUnit Test Case
– Make sure New JUnit 4 test is selected
– Click where it says “Click here to add JUnit 4...”
– Close the window that appears
• To create a JUnit test class:
– Do steps 1 and 2 above, if you haven’t already
– Click Next>
– Use the checkboxes to decide which methods you want test cases for;
don’t select Object or anything under it
• I like to check “create tasks,” but that’s up to you
– Click Finish
• To run the tests:
90 – Choose Run  Run As  JUnit Test

Viewing results in Eclipse


Ran 10 of No tests Something unexpected
Bar is green if the 10 tests failed, but... happened in two tests
all tests pass,
red otherwise

This is how
This test passed long the
test took

Something is wrong

Depending on your
preferences, this
window might show
only failed tests
91

44
Recommended approach

• Write a test for some method you intend to write


– If the method is fairly complex, test only the simplest case
• Write a stub for the method
• Run the test and make sure it fails
• Replace the stub with code
– Write just enough code to pass the tests
• Run the test
– If it fails, debug the method (or maybe debug the test); repeat
until the test passes
• If the method needs to do more, or handle more complex
situations, add the tests for these first, and go back to
step 3
92

The End

If you don't unit test then you aren't a software engineer, you
are a typist who understands a programming language.
--Moses Jones

1. Never underestimate the power of one little test.


2. There is no such thing as a dumb test.
3. Your tests can often find problems where you're not
expecting them.
4. Test that everything you say happens actually does happen.
5. If it's worth documenting, it's worth testing.

--Andy Lester

93

45
THE END

46

You might also like