Key Ideas
[Part 11 of 11]


In this part's key ideas, we'll look at Unit Testing and making an executable for Windows, but we'll also experiment with using libraries downloaded from the internet and look at some additions to the language which we haven't yet looked at -- specifically static imports, varargs, and annotations (these were added in Java 1.5 -- for more details see the mop-up page).


Unit testing: This section will show you how to build and run a basic unit test.

Here are the classes we'll use for convenience (note that they are packaged, so you'll need a root directory containing a classes directory and a tests directory so you can save these in the appropriate directories): classes.Testee.java; tests.TestClass.java; tests.TestRunner.java.

 

  1. To do Unit Testing, you'll need a Unit Testing library. The most commonly used is JUnit. You'll need to download the two libraries: junit.jar and hamcrest-core.jar. Save them to a directory you can get to easily. Here, we'll assume it's c:\junit\. These jar files are essentially zip files containing the classes we need. Leave them zipped up.

  2. Then, we'll need a class to test. Here's a class with two methods in it. Both methods should return the sum of two numbers, and that's what we'll test for, but note that the second has an error -- a "+" has been accidentally entered as a "-"...

     

    package classes;

    public class Testee {

       public int goodAdd (int num1, int num2) {
          return num1 + num2;
       }

       public int badAdd (int num1, int num2) {
          return num1 - num2;
       }

    }

    Note that it is in a classes package.

  3. Secondly, we'll need a class to do the testing. Note that we'll put this in a different package from the class above, so we don't need to bundle it with any code we end up distributing to users.

     

    package tests;

    import classes.Testee;
    import org.junit.*;
    import static org.junit.Assert.assertEquals;

    public class TestClass {

       Testee testee = new Testee();
       int a = 10;
       int b = 20;

       @Test
       public void testMethod() {
          assertEquals("Problem with add.", a + b,testee.goodAdd(a,b));
          assertEquals("Problem with badAdd.", a + b,testee.badAdd(a,b));
       }

    }

    There's a few things in here that it is worth highlighting.

    1. Note that we import the Testee class's package so we can make an object from it.
    2. Note that we also import a method Assert.assertEquals. Since java 1.5 you've been able to import parts of a class, but only the static parts (details). Here, the import allows us to use assertEquals without even referencing the Assert class.
    3. Third, note the new @Test tag. This is a bespoke tag added by the JUnit framework. Such bespoke tags are called "Annotations". They take a bit of setting up, but you can find more details on this Oracle website. In this case, the JUnit system will run through our test class running each method annotated with @Test. Note that the information about the annotations is in org.junit so we have to import this package.

  4. The final thing we need is a class that will run all the test classes we may have. Here it is:

     

    package tests;

    import org.junit.*;
    import org.junit.runner.*;
    import org.junit.runner.notification.*;

    public class TestRunner {

       public static void main(String[] args) {

          Result result = JUnitCore.runClasses(TestClass.class);
          for (Failure failure : result.getFailures()) {
             System.out.println(failure.toString());
          }
          System.out.println("Time to run = " + result.getRunTime() + "ms.");
          System.out.println("It is " + result.wasSuccessful() + " that all runs succeeded.");
       }

    }

    Note that like so many things in computing (e.g. compiling java classes), only failures are really reported with any enthusiasm. Note also that the JUnitCore.runClasses method uses the ... "varargs" option for its intake parameters. We haven't covered this, mainly because it has a tendancy to result in coding errors if used without fairly strict control, but ... allows an undefined number of inputs to a method. In this case, JUnitCore.runClasses can take in one or more classes to run with @Test methods inside. So, for example, if you had more test classes, you'd say:

    Result result = JUnitCore.runClasses(TestClass1.class, TestClass2.class);

    This isn't polymorphism, it is a genuinely flexible method. Note that, as written, the Result object would hold the results associated with all these tests. You can find out more about the ... "varargs" option on this Oracle webpage.

  5. Finally, we need to compile and run our classes. Because we're using two external libraries of classes (inside the jar files) we need to add these to our CLASSPATH, so the compiler and JVM knows where they are. You could do this by adding them to the system CLASSPATH (see the JDK setup page), but here we'll add them at the command line. Assuming that we're in the root directory containing both our tests and classes package directories, and our jar files (junit-4.11.jar and hamcrest-core-1.3.jar) are in c:\junit\, the command to compile would be:

    javac -cp c:\junit\junit-4.11.jar;c:\junit\hamcrest-core-1.3.jar classes\*.java tests\*.java

    Note that this compiles both classes\*.java and tests\*.java

    The command to run is then:

    java -cp c:\junit\junit-4.11.jar;c:\junit\hamcrest-core-1.3.jar tests.TestRunner

  6. The test should return nothing about goodAdd but return that there was an issue with badAdd. It should also return that at least one issue was found, and the time it took to run the tests. Fix badAdd and see the difference when all runs ok.

 


Making an executable: This section will show you how to make an executable for Windows using Launch4J. Plainly you'll want to supply additional versions for Linux-based systems.

Here are the files we'll use for convenience: HelloWorld.java; MANIFEST.MF. Save them in the same directory as each other.

  1. First, download and install Launch4J. Note that you may have problems installing it into a directory with spaces in the names (e.g. "Program Files"). If you get problems, install it into c:\Programs\Install4J\; it will create the necessary directories. If you have trouble installing it because of permissions, just download and unzip the zipped version.
  2. Install4J wraps around a self-executing jar file, so we have to make one of these first. For this, you need two things: your class files, and a "Manifest" file. The Manifest is the main difference between a jar file and a zip file. It usually contains very little, however, if we include in it an indication of the main class file, our jar file should automatically run when clicked on in Windows Explorer. Under normal circumstances, Windows (and other operating systems) should run such "self-executing jar files" using javaw.exe, which is a version of the JVM without the command prompt 'black box'. We're going to utilise a single java file: HelloWorld.java (though if you look at it you'll see it actually generates two classes, because it has an anonymous inner class). Our manifest file will look like this:

    Manifest-Version: 1.0
    Main-Class: HelloWorld


    You can download the file here: MANIFEST.MF. It is worth noting a couple of things about the file that won't be immediately obvious. The first is that it should be encoded in UTF-8. On Notepad++ this option can be found under the Encoding menu: you want UTF-8 without BOM -- you'll need to resave the file if you've written it yourself in a different encoding. Secondly the file should end with a new line or carriage return character. The simplest way to guarantee this is to put a couple of blank lines at the bottom of the file.

    Save both the HelloWorld.java and MANIFEST.MF files to the same directory, compile the java file into classes, and then put them in a jar file, thus (you can find out more about making jar files on the jar tool webpage):

    jar cvfm HelloWorld.jar MANIFEST.MF *.class

    You should then be able to find the file in Windows Explorer, and click it to run.

    If this works, you might think this is enough, and, indeed, for some people it is. However, if your copy of Windows isn't set up to run jar files, or you're missing the Java Runtime Environment or JDK, executable jars won't run. Under these circumstances you need something that will check for the JRE/JDK and offer to install it if it is missing. This is where you need Install4J or an equivalent. This should ideally also make sure the jar runs and attach it to an icon to make it easy for users.

  3. If you've managed to successfully install Install4J, open it up. If there's isn't an icon for it, find where it is installed and run launch4j.exe by double-clicking on it in Windows Explorer. You should see a tabbed GUI. Under the Basic tab, set the Output file so that it is pointing at the directory where you want to save your application, with the filename HelloWorld.exe. Set the Jar option to the location and filename of the jar file you created above. Under the JRE tab, set the Min JRE version to 1.1.0. These are the most basic settings.
  4. Now push the little icon on the toolbar that looks like a cog. This will build your executable. You should be able to find the outputed HelloWorld.exe and run it by double clicking on it.
  5. This is the most basic setup, and will divert the user to the JRE download page if they haven't got one installed. However, there are many more options you can add, including bundling up a JRE and adding desktop icons etc. You can find out more details on the Launch4J documentation site.