Session 13

We started by talking about some issues from HW#2.

 

Testing and debugging

During HW2, I asked you to consider how you might test that your classes works properly.  This seems like a simple question, but it is not always easy.  We expect students to test, but we don't often do a good job teaching you how to test.

Today we tried to scratch the surface of this issue.

While we won't spend a huge amount of time on this (really, only today FORMALLY) I hope you don't come away from this thinking that I think that this isn't important.  Just the opposite.  I suspect that testing is probably one of the most underrated skills and abilities you can develop as a programmer.  Just to give you a clue as to how serious a subject this is, our department offers an ENTIRE SEMESTER course on testing (810:175).

 

When we talk about testing, we are really talking about one of two kinds of error detection. 

The first type of error detection is one that most of you are already getting pretty good at.  That is, checking for syntax errors.  These are easy to catch (although not always easy to fix) because you hit the compile button on your IDE or type the compile command at the console, and the computer tells you if there are problems. 

The second type of error detection is the one that I am really interested in today.  This is the detection of logic errors.  These are errors where the computer "works."  But it doesn't work correctly. 

In the rest of the discussion today we will talk about how to test for and try to fix this second kind of error.

Types of Testing

There are several ways to divide up the types of testing that we do on software systems:

You can also consider:

Positive testing is the testing of cases that are expected to succeed and confirming that you get the answer you expected.   

Examples of positive tests from HW #1

 

Negative testing is the testing of cases where you expect your code to 'fail' the situation.  In many cases these are the conditions that produce exceptions or error messages.  You want to confirm that your code will produce these exceptions or error messages and handle them gracefully. 

 

There is another vocabulary word we should talk about with all of this.  That is the concept of boundary testing.   Boundary testing is not a third type of testing.  Instead, it is a special case of each of the previous two tests.  Boundary testing is the idea of testing the "first" positive test case and the "first" negative test case when there are boundaries between several positive cases or between a positive case and a negative case. 

For example,

 

 

 

Automated Testing Using JUnit :

One of the techniques for writing good code is to START by considering what the test cases would look like.  Then as you write your code you immediately test it with the appropriate cases. 

That sounds easy, but what do you do when your program is large?  In particular, what should you do when you are working on functionality late in the process but you need to modify some of the earlier functionality.  You REALLY need to go back and run all the tests you have run so far to confirm that everything STILL works.

Manual testing as described above can be very time consuming and potentially error prone.  Because of that, programmers have turned to techniques of automated testing.

One way to write automated tests is through sometimes elaborate client code that runs through all sorts of situations.  To be honest, this is what I normally do, but it can become incredibly complicated.

An alternative is to use a testing process/suite such as JUnit.

JUnit is a third-party package of code that works as a "plug in" of sorts along with your java compiler.  While you can configure most modern java IDEs to include JUnit tests, today we will look at a mechanism for running this from the command line:

To do this:

  1. Download the JUnit jar file from the developers of JUnit (www.junit.org).  The version as of yesterday was junit-4.5.jar.
  2. Place this wherever seems appropriate.  If you are doing this long term you would install it in a fixed location.  If you are doing this for a particular project then you can place the file inside of the directory with the code you are writing (I demoed this in class with code from HW02
  3. Write one or more test classes in the following format:
      import org.junit.*;
      import static org.junit.Assert.*;
      
      public class DiceJUnitTest
      {
      	@Test
      	public void test_defaultSize()
      	{
      		System.out.println("Test if the default size is 6");
      		Die d1 = new Die();
      		assertTrue( d1.howManySides()==6 );
      	}
     
      	@Test
      	public void test_rollInRange()
      	{
      		System.out.println("Do rolls produce values in range?");
      		Die d1 = new Die();
      		for (int i=0; i<100; i++)
      		{
      			int value = d1.roll();
    			assertTrue( value>0 && value<7 );
    		}
      	} 		
      }
      
    ************** We stopped here and will finish the rest in class on Monday ******
  4. Compile your test class by typing:
      prompt> javac -cp ".;junit-4.5.jar" DiceJUnitTest.java
      
  5. Run your test class by typing:
      prompt> java -cp ".;junit-4.5.jar" org.junit.runner.JUnitCore DiceJUnitTest
      

Please note that you can run more than one suite of tests at once by including multiple commands

 

 

Finally, if you have some "setup" you want to do before each test (sort of like a constructor) you can write something like

    private Die d1; 
        
    @Before public void setUp()
    { 
        d1= new Die();
        while (d1.roll() != 3) 
        {} 
    }

 

For more reading check out:

 

 

I showed you the tester I wrote for Student.java from HW#2

I gave you additional instructions for HW#3