JUnit testing
Current practice print statements, debugger expressions, test scripts. Writing expressions in a debugger only that t a program works now. An application typically undergoes many changes over the years by many different programmers. Problem how to make sure that the programs is testing whenever there is a change?
Arguments for automated testing Prevailing assumption => if a developer says a program feature works, then it works now and forever. Often there are too many deadlines and testing is not adequate. Prevailing assumption => if a developer says a program feature works, then it works now and forever.
Arguments for automated testing XP philosophy => If a program feature lacks an automated test, it doesn t work. Developers aren t done when they write and debug the code, they must also write tests that demonstrate that the program works. goal is to write a framework that developers will actually use. use familiar tools it has to eliminate duplicated effort.
JUnit philosophy The idea is get assurance that the program will work in five years, when the programmer is long gone. The second goal is creating tests that retain their value over time. Someone other than the original programmer has to be able to execute the tests and interpret the results. It should be possible to combine tests from various authors and run them together without fear of interference.
Idea of JUnit testing We need to run our code to verify that it actually works as intended. A new change may break the existing code. Verifying all features by hand is a menial and time consuming task. Unit tests which are small snippets of code designed to exercise a particular class. With unit tests in place, when a change is made to the code we can simply run all the tests to ensure that nothing has been broken.
Web sites to look at http://www.cs.umanitoba.ca/~eclipse/10-junit.pdf p p http://www.cs.umanitoba.ca/~eclipse/eclipse3-1.pdf See interactive video tutorials are available at See interactive video tutorials are available at eclipsetutorial.sourceforge.net/totalbeginner.html lessons 4, 5, 6, 7 and 8
Adding Tests to TestCases Any method in a TestCase class is considered a test if it begins with the word test You can write many tests (have many test methods) Each test method should use a variety of assert methods to test things about the state of their classes under tests Assert methods are inherited
Assert Methods Assert methods include: assertequal(x,y) assertfalse(boolean) asserttrue(boolean) assertnull(object) t) assertnotnull(object) assetsame(firstobject, tobj t secondobject) assertnotsame(firstobject, secondobject)
Why Unit testing? Code components must be tested! Confirms that your code works. What if it does not? Components must be tested in isolation. A functional test can tell you that a bug exists in the implementation But where is the bug? A unit test tells you where the bug is located
Ideas A unit is the smallest testable component in an application A unit is in most cases a method A unit does not depend on other components which h are not unit tested t themselves Focus on whether a method is following its API contract
Unit testing
Fundamentals of JUnit Your test class should extend the TestCase class Will find and execute all methods starting with test in your test class Lets you set up a test fixture by overriding the setup and teardown methods. setup is called to create the enviornment for testing. Method teardown is typically used tpo close data base etc after each test. Provides methods for verifying i method output t through the Assert class
Features Tests may require common resources to be set up Complex data structures Database connections A fixture is a set of common needed resources Common setup code inside tests doesn t make sense A fixture can be created by overriding the setup and teardown methods from TestCase setup is invoked before each test, teardown after each test.
public class Person { private String name; private int age; public Person(){ name = "Unknown"; age = -99; public String getname() { return name; public void setname(string name) { this.name = name; public int getage() { return age; public void setage(int age) { this.age = age;
import junit.framework.testcase; public class PersonTest extends TestCase { public void testperson() { Person p1 = new Person(); assertequals("unknown", p1.getname()); assertequals(-99, p1.getage()); public void testsetname() { Person p3 = new Person(); p3.setname("john"); assertequals("john", p3.getname()); public void testsetage() { Person p4 = new Person(); p4.setage(25); assertequals(25, p4.getage());
The Assert class Contains methods for testing whether Conditions are true or false Objects are equal or not Objects are null or not If the test fails, an AssertionFailedError is thrown All methods have overloads for various parameter types Methods available because TestCase inherits Assert
Assert Methods Assert methods include: assertequal(x,y) two objects are equal. assertfalse(boolean) etc asserttrue(boolean) assertnull(object) assertnotnull(object) assetsame(firstobject, secondobject) assertnotsame(firstobject, ts tobj t secondobject) fail( String ) Asserts that a test fails, and prints the given message.
A more comprehensive example Shows how exception may be handled use of setup method
public class Child { private String name; private int age; public Child(){ name = "Unknown"; age = 0; public Child(String name, int age) throws Exception{ if (age > 18) throw new Exception("Wrong age"); this.name = name; this.age = age; public int getage(){ return age; public void setage(int age){ this.age = age;
public void setname(string name){ this.name = name; public String getname(){ return name; public String tostring(){ return ("Name " + this.getname() + "; age = " + this.getage());
import junit.framework.testcase; public class ChildTest extends TestCase { Child c; protected void setup() throws Exception { super.setup(); c = new Child(); public void testchild() { assertequals(c.getage(), 0); assertequals("unknown", c.getname());
public void testchildstringint() { Child c1; try { c1 = new Child("John", 20); fail("constructor should raise an Exception"); catch (Exception e){ System.out.println("Expected exception)"); try { c1 = new Child("Mary", 9); assertequals("mary" Mary, c1.getname()); assertequals(9, c1.getage()); catch (Exception e) { fail("constructor should not raise an Exception");
public void testsetage() { c.setage(15); assertequals(15, c.getage()); public void testsetname() { c.setname("tom"); assertequals("tom" Tom, c.getname()); public void testtostring(){ assertequals("name Unknown; age = 0", c.tostring());
Test Driven Development Test-driven development(tdd) uses short iterative development cycles. Before writing any code, you must first write an automated test for your code. While writing the automated tests, you must take into account all possible inputs, errors, and outputs. This way, your mind is not clouded by any code that s already been written.
TDD (Cont d) The first time you run your automated test, the test should fail indicating indicating that the code is not yet ready. Afterward, you can begin programming. Since there s already an automated test, as long as the code fails it, it means that it s still not ready. The code can be fixed until it passes all assertions. Once the code passes the test, you can then begin cleaning it up, via refactoring. As long as the code still passes the test, it means that it still works. You no longer have to worry about changes that introduce new bugs. Start the whole thing over again with some other method or program. After any change to the code, run your tests.
Why is TDD useful? Have you ever purposefully skipped testing a program because: You felt it was a waste of time to test, since it was only a slight code change? You felt lazy testing ti everything again? You didn t have enough time to test because the project manager wanted it moved up to production ASAP? You told yourself you d do it tomorrow? o o You had to choose between manual testing, or watching the latest episode of your favorite TV show
TDD Most of the time, nothing happens, and you successfully move your code to production without any problems. But sometimes, after you ve moved to production, everything goes wrong. You re stuck fixing a hundred holes in a sinking ship, with more appearing every minute. You do not want to find yourself in this situation. When creating your tests. For this, you really need to plan and think about what your function will do, what possible inputs it will get, and the corresponding outputs it will send. This step resembles playing a game of chess you need to know everything about your opponent (the program), including all his weaknesses (possible errors) and strengths (what happens if it successfully runs). See http://en.wikipedia.org/wiki/test-driven_development / iki/t t d t
I chose to include the setup and constructor methods. The resulting window is shown in the next slide.
Analysis: Eclipse is complaining that I have not defined any tests yet!! OOPS! I now introduce a testconstructor tc t t method and try again. I got the message: junit.framework.assertionfailederror: No tests found in mypackage.linkedlisttest
Notice that there are two compilation errors. I ignored them and got a failure.
When I clicked on the first error, I got these suggestions from code assist. Clearly the problems arose because I did not create the class MyLinkedList. I double clicked on the first suggestion and got the window on the right. The folder should be src. I corrected it by choosing browse and pressed finish.
This is what I got a new class called MyLinkedList.java. I moved to MyLinkedListTest.java and ran JUnitTest again.
It succeeded. Why? The test just requires that the constructor must create a non-null object.
Suppose I now feel that when created the size of the list must be zero. To do this, I changed the test class as shown below. Notice the cross? It does not like getsize()!
I clicked on the cross and got the following. I accepted the first choice So now Eclipse automatically inserted an empty method getsize which returns an object of class Object. I fixed the return type to int giving the following window.
We now have to fill in the details of the method. Just to show what Eclipse can do, I said return size. Notice the cross beside this line?
Code assist gave the suggestions shown. I picked Create field size and got the following
Add a contructor We added a constructor creating an empty list. start must point to an empty list. how to test if this is working. the size of the list must be empty. The testing class includes this assertion.
I decided that an object of MyLinkedList should be a linked list of objects of class Node and there should be a pointer to the first object of class Node. Now I am changing MyLinked list to include the private inner class Node and added a new variable to point to the first object of class Node in the linked list. My definition is now package mypackage; public class MyLinkedList { private class Node{ Object value; Node nextpointer; public String tostring(){ return value.tostring(); private int size; MyLinkedList start; public MyLinkedList(){ start = null; public int getsize() { return size;
Success again
public class MyLinkedList { private class Node{ Object value; Node nextpointer; I should have tested Node class in the same way. public String tostring(){ return value.tostring(); MyLinkedList start; public MyLinkedList(){ start = null;
import junit.framework.testcase; public class MyLinkedListTest extends TestCase { public MyLinkedListTest(String name) { super(name); protected void setup() throws Exception { super.setup(); public void testconstructor(){ MyLinkedList list; list = new MyLinkedList(); assertnotnull(list); assertequals(0, list.size()); Of course this test fails because we did not define size. When we compiled, Eclipse complained as shown in next slide
Just to show what happens I said Proceed. Not surprisingly, the test failed.
We now have to add the size method
public class MyLinkedList { private int size; private class Node{ Object value; Node nextpointer; public String tostring(){ return value.tostring(); MyLinkedList start; public MyLinkedList(){ start = null; size = 0; public int getsize(){// I decided that size() is not appropriate return size;
import junit.framework.testcase; public class MyLinkedListTest extends TestCase { public MyLinkedListTest(String name) { super(name); protected void setup() throws Exception { super.setup(); public void testconstructor(){ MyLinkedList list; list = new MyLinkedList(); assertnotnull(list); assertequals(0, list.getsize()); // I did not include a test for getsize // since it is being tested anyway
Problem to consider Add a new node