This article is old and is being consolidated into the book.
Please refer to the corresponding chapter(s) therein.
If the chapters or sections are not completed yet, you can use this article.
Refer to the examples as they are tested against the latest code.
Java Software Unit TestingBy James Jianbo Huang February 2002 non-printer versionAbstract Unit testing is critical to the quality of object-oriented software, because it solidifies the foundation -- the objects. This article explains the uses of a unit testing package for Java called JUnit, discusses the pros and cons of packages like such, and introduces how to do unit testing with JudoScript and explain why it is the way to go.
In this article, I will explain a Java unit testing package called JUnit, what it is and what it does. I will discuss issues regarding its approach, draw some clear-cut conclusions, and present unit testing in JudoScript to show that this is the way to go. 1. Java Unit Testing with JUnit
JUnit is a Java development package for writing Java code to test other Java software. In essence, it formalizes test cases and test suites in the form of Java classes, and provides a few assertion and reporting facilities, so that Java classes for test cases are developed consistently. Since it is a testing tool, it can potentially be used by three types of engineers:
For experienced Java programmers, it probably takes less than an hour to start writing useful testing code with JUnit, because JUnit is just another Java package, and programmers use various Java packages day-in-day-out. If you do JUnit programming already, simply skip to the next section. If you never program in Java, you are not up to using JUnit. Period. You can go to section 3 to learn how to use JudoScript for unit testing; otherwise, you will have to learn Java first. This section is for Java-savvy QA engineers. First off, it is a good
idea to print out the following image, the class diagram of JUnit's key
package, ![]() A class diagram depicts the relationships among classes. For instance,
in the middle of this diagram lies the most important classes,
To write a test case, you write a Java class that extends public class SSHFactory { public static void scp(String host, String user, String password, String ciper, String[] src, String dest, boolean recursive, boolean toremote) throws Exception { ... ... ... } } The following is a run of a test case:
For the test case, all you have to do is override the
In this case, we implemented a method called "testSCP_NonRecursive";
then, in In real world testing, many test cases require certain settings. For
instance, if the remote host is not running SSH when we run the test case,
it will fail but that is not a bug; it is a user error. Or the test is run
on a different PC that does not contain "temp/file1.txt" -- another user
error. In JUnit, you can set up and clean up these "test fixture" by
overriding methods To automate testing, JUnit provides class import junit.framework.*; import com.judoscript.ext.SSHFactory; import java.io.*; public class MyTestCase3 extends TestCase { public MyTestCase3(String method) { super(method); } public void testSCP_NonRecursive() { ... ... ... } public void testSCP_Recursive() { ... ... ... } protected void setUp() { // set up the test directories and files on the local machine ... ... ... } protected void tearDown() { // remove the test directories and files on the local machine ... ... ... } public static void main(String[] args) { try { TestSuite suite = new TestSuite(); suite.addText(new MyTestCase3("testSCP_NonRecursive")); suite.addText(new MyTestCase3("testSCP_Recursive")); TestResult tres = suite.run(); System.out.println("# of failures: " + tres.failureCount()); } catch(Exception e) { e.printStackTrace(); } } } // end of class MyTestCase3. This code is straightforward. The class itself implements two test
cases. In JUnit is a simple idea and simple framework. It tries to standardize the way people write test cases and suites for Java, in Java. In fact, in the article JUnit A Cook's Tour, it is clearly stated, "the number one goal is to write a framework within which we have some glimmer of hope that developers will actually write tests." Assuming developers are only willing to write Java code, JUnit cuts their excuses not writing any tests. The question is, is this a blessing for the QA team?
2. Problems with Writing Test Cases in Java
In a team environment, tests are run and maintained by QA engineers, not developers. Writing tests in Java has at least the following three problems. The root of the problems is, Java is a system language, designed for building robust software, not for doing things directly and quickly.
Therefore, Java is a wrong language for general testing; designing a formal framework does not help. It will
If some developer decides to use JUnit for his/her own testing, it is perfectly fine. For a team with developers and QA testers, even if all the QA testers are fully Java potent, this approach still slows down the pace and productivity and should never be adopted. What is the solution then? Scripting! Scripting languages are designed to do many things easily, quickly and effectively; they are perfect for testing. Since they are also general-purpose programming langauges, they are up to almost anything. In fact, conceptualizing tests as just test cases and test suites may not be enough, because it limits the ways test cases be organized. For instance, I may want to connect to a test database, create a few test tables with data, then test a series of test cases before I drop all the test tables and disconnect from the database server. Such tasks are better left to the QA testers who do the testing. Testing is a step-by-step process of execution and analysis. There is no shortcut, complicated architectures bring in little value. Powerful scripting languages are the tool for testing.
3. Java Unit Testing with JudoScript
For Java software testing, JudoScript is a perfect match for these reasons:
As we mentioned earilier, there are many different set-ups for testing. The following following scheme have multiple test cases share a same pair of set-up/clean-up routines: This is best modeled in JudoScript using functions:test environment verify and setup; // make sure the test database server // is live and make a connection. run test case 1; run test case 2; run test case 3; test environment cleanup; // disconnect from the database function test_Foo_setup() { connect 'jdbc:oracle:thin@localhost:1521:mydb', 'orauser', 'orapass'; } function test_Foo_cleanup() { disconnect(); } function test_Foo_1() { ... } function test_Foo_2() { ... } function test_Foo_3() { ... } // run the test: test_Foo_setup(); test_Foo_1(); test_Foo_2(); test_Foo_3(); test_Foo_cleanup(); This is another scenario: test environment verify and setup; test set up A; run test case 1; test clean up A; test set up A; run test case 2; test clean up A; test set up B; run test case 3; test clean up A; run test case 4; test environment cleanup; In addition to the global setup/cleanup, many test cases have their own environment; some of the test cases share a same pair of setup/cleanup routines. In this case, the inheritance feature of object-oriented programming helps to orginize code nice and clean: function test_Foo_setup() { ... } function test_Foo_cleanup() { ... } class Test_Base { function setup() {} // empty function cleanup() {} // empty function doTest() {} // empty -- should be overridden function runTest() { setup(); doTest(); cleanup(); } } class Test_Foo_Group_A extends Test_Base { function setup() { /* group A setup */ } function cleanup() { /* group A cleanup */ } function doTest() {} // empty -- should be overridden } class Test_Foo_Group_B extends Test_Base { function setup() { /* group B setup */ } function cleanup() { /* group B cleanup */ } function doTest() {} // empty -- should be overridden class Test_Foo_1 extends Test_Foo_Group_A { construtor { desc = "Project Foo's test case 1."; } function doTest { /* test case 1 code */ } } class Test_Foo_2 extends Test_Foo_Group_A { construtor { desc = "Project Foo's test case 2."; } function doTest { /* test case 2 code */ } } class Test_Foo_3 extends Test_Foo_Group_B { construtor { desc = "Project Foo's test case 3."; } function doTest { /* test case 3 code */ } } class Test_Foo_4 extends Test_Base { construtor { desc = "Project Foo's test case 4."; } function doTest { /* test case 4 code */ } } // Run the tests test_Foo_setup(); (new Test_Foo_1).runTest(); (new Test_Foo_2).runTest(); (new Test_Foo_3).runTest(); (new Test_Foo_4).runTest(); test_Foo_cleanup(); The class hierarchy is like this: Test_Base Test_Foo_Group_A Test_Foo_1 Test_Foo_2 Test_Foo_Group_B Test_Foo_3 Test_Foo_4 In effect, the test cases are grouped; each group shares a pair of
The second scenario can be implement with functions as well. The
point is, JudoScript is flexible enough for you to lay out tests in the most
appropriate way for the situation. JudoScript language has built-in assertion
and exception handling facility. It also has standard, error and log
output streams. If you have some special reporting needs, you can
easily write your own report functions, perhaps using the the flexible
To truly understand JudoScript language and feel its power, there is no other way than reading featured articles such as Introduction to JudoScript, and try it out by yourself. JudoScript is an easy-to-use and easy-to-learn language; even if you have never programmed in Java, you won't have any problem start writing useful JudoScript code.
4. Code Listings
|