Thursday, December 16, 2010

Program 12 (ABC) - Program Test Data, Debugging and Termination: Program Termination and Return Codes



I selected Java, Visual C++, and Ruby for this assignment. I am somewhat familiar with Java, having just completed a introductory course. However I for this assignment I have referenced the expert official developer sources for each of these languages to find the best information on testing, debugging, and program error codes.

JAVA
For Java, I went to the following website: http://www.parasoft.com/jsp/products/article.jsp?articleId=3108, as well as Java Sun's website. The following passage is from Parasoft:

Static Code Analysis, Data Flow Analysis, Unit Testing, Runtime Error Detection, and Code Review

Software verification techniques such as pattern-based static code analysis, runtime error detection, unit testing, and flow analysis are all valuable techniques for finding bugs in Java programs. On its own, each technique can help you find specific types of errors. However, if you restrict yourself to applying just one or some of these techniques in isolation, you risk having bugs that slip through the cracks. A safer, more effective strategy is to use all of these complementary techniques in concert. This establishes a bulletproof framework that helps you find bugs which are likely to evade specific techniques. It also creates an environment that helps you find functional problems, which can be the most critical and difficult to detect.

Development can try to debug it, but debugging on the production server is time-consuming and tedious. They would need to step through each statement of business logic as it executes in hopes of spotting the point where the runtime behavior deviates from their plan. Even if they find the point where plan and reality diverge, the underlying cause may not be apparent. Alternatively, they might apply certain tools or techniques proven to pinpoint errors automatically.

At this point, the developers can start crossing their fingers as they try to debug the application with the debugger. Or, they can apply an automated testing strategy in order to peel errors out of the code. If the application is still not working after they try the automated techniques, they can then go to the debugger as a last resort. The Java manual at Sun.com, provide the following instructions for testing.”

This passage makes the argument that all programmers need a separate debugging application (which ParaSoft sells) to find all the errors. However, according to Java.Sun.com, everything needed for debugging is already in the Java language library itself. Detailed instructions on running a test from the Java.Sun.com Java Developer’s Manual follow:

JAVA MAIN() TESTING
“A common way that many Java developers exercise objects is to create a main method
that instantiates an instance of the class, and performs a series of checks to ensure that
the object is behaving as desired. For example, in our HtmlDocument class we define
a main method as:

public static void main(String args[]) throws Exception {
HtmlDocument doc = new HtmlDocument(new File(args[0]));
System.out.println("Title = " + doc.getTitle());
System.out.println("Body = " + doc.getBodyText());
}

We are then able to run the program from the command-line, with the proper classpath
set:

java org.example.antbook.ant.lucene.HtmlDocument
test/org/example/antbook/ant/lucene/test.html
Using Ant as a Java program launcher, we can run it with the <java> task:

<java classname="org.example.antbook.ant.lucene.HtmlDocument">
<arg value="test/org/example/antbook/ant/lucene/test.html"/>
<classpath refid="test.classpath"/>
</java>

Writing main method checks is convenient because all Java IDEs provide the ability
to compile and run the class in the current buffer, and certainly have their place for
exercising an object’s capability. There are, however, some issues with this approach
that make it ineffective as a comprehensive test framework:

• There is no explicit concept of a test passing or failing. Typically, the program
outputs messages simply with System.out.println; the user has to look at
this and decide if it is correct.
• main has access to protected and private members and methods. While
you may want to test the inner workings of a class may be desired, many tests
are really about testing an object’s interface to the outside world.
• There is no mechanism to collect results in a structured fashion.
• There is no replicability. After each test run, a person has to examine and interpret
the results.”


VISUAL C++
For Information on Visual C++, I went to the Microsoft Developer’s website. There are several ways to perform tests and debugging in Visual C++ the following passages outline just two methods from the official Developer website:

“Using Assert Statements in Unit Tests
By default, each generated unit test calls the Inconclusive method, which causes the test to fail because the test is still essentially unimplemented. Your next step is to add meaningful code to check the correct operation of the method being tested. A typical way to do this is to generate a value and then compare it with an expected value by using an Assert.AreEqual statement. For an example, see "Unit Test Example" in Structure of Unit Tests. Newly generated unit tests contain "To-do" comments that suggest changes to make.
A unit test that contains no Assert statement automatically passes as long as it does not time out and does not throw an unexpected exception. For more information, see Basic Test Results and Using the Assert Classes.

Opening and Authoring Unit Tests
This topic contains two procedures:
·             The first procedure describes how to edit an existing unit test. You typically do this to prepare a unit test that has been generated automatically. See How to: Generate a Unit Test.
·             The second procedure describes how to create and author a unit test by hand.

To edit an existing unit test

1.       In your test project in Solution Explorer, locate and open the file that contains the unit test, and then locate the unit test method that you want to edit.
- or -
In Test View, double-click the unit test; this opens the file that contains the unit test and scrolls to the unit test method.
2.       Locate the variable assignments in the method.
In newly generated tests, variable assignments are marked by "To-Do" statements that remind you to customize the assignments. For example, the following is a typical assignment that needs to be edited:
string target.owner = null; // TODO: Initialize to an appropriate value
3.       Assign an appropriate value to each variable.
To know what values are appropriate, consider the values that these variables may be initialized to before the method is called, the changes they may undergo when the method is called, and the results you expect. For an example of this process, see the procedure Run and Edit a Unit Test in Walkthrough: Creating and Running Unit Tests.
4.       Locate and edit the Assert statements in the method. If necessary, add additional Assert statements.
The Unit Testing Framework provides numerous additional Assert classes and methods that give you flexibility in writing useful Assert statements. For more information, see Unit Testing Framework.

To create a unit test by typing it in

1.       In Solution Explorer, right-click a test project, point to Add, and click New Test.
- or -
Right-click the surface of the Test View window and then click New Test.
This displays the Add New Test dialog box.
2.       Under Templates, click Unit Test and then click OK.
A new source code file with a name such as UnitTest1.cs is added to your test project, in the language of the test project. This file contains several things that unit tests require:
·             It references the Microsoft.VisualStudio.TestTools.UnitTesting namespace and the System namespace.
·             It defines its own namespace, which contains a test class. Test classes have the [TestClass] attribute.
·             It contains an initialization method and a cleanup method. These methods have the [TestInitialize()] and [TestCleanup()] attributes, respectively.
·             It contains one empty test method, with a [TestMethod] attribute. It is here that you add your test logic. This method has a default name such as TestMethod1().
This file is also opened in the window for editing source code. The new (empty) test method is displayed in the Test View and Test Manager windows.
3.       Add test code to the test method.

The Unit Testing Framework provides numerous additional Assert classes and methods that give you flexibility in writing useful Assert statements. “


RUBY
Ruby provides a testing tool called TestUnit. It seems to be the most simple of all the different languages I have listed above. The Ruby website, has a one page process on how to run the test. The passage below outlines their instructions:

"Test::Unit wraps up a collection of test methods together and allows you to easily set up and tear down the same test fixture for each test. This is done by overriding setup and/or teardown, which will be called before and after each test method that is run. The TestCase also knows how to collect the results of your assertions into a Test::Unit::TestResult, which can then be reported back to you… but I‘m getting ahead of myself. To write a test, follow these steps:
References:

http://java.sun.com/developer/Books/javaprogramming/ant/ant_chap04.pdf

http://www.parasoft.com/jsp/products/article.jsp?articleId=3108
http://msdn.microsoft.com/en-us/library/ms243171(VS.80).aspx
http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html



  • Make sure Test::Unit is in your library path.
  • require ‘test/unit’ in your test script.
  • Create a class that subclasses Test::Unit::TestCase.
  • Add a method that begins with "test" to your class.
  • Make assertions in your test method.
  • Optionally define setup and/or teardown to set up and/or tear down your common test fixture.
  • You can now run your test as you would any other Ruby script… try it and see!
A really simple test might look like this (setup and teardown are commented out to indicate that they are completely optional):
    require 'test/unit'

    class TC_MyTest < Test::Unit::TestCase
      # def setup
      # end

      # def teardown
      # end

      def test_fail
        assert(false, 'Assertion was false.')
      end
    end

Test Runners

So, now you have this great test class, but you still need a way to run it and view any failures that occur during the run. This is where Test::Unit::UI::Console::TestRunner (and others, such as Test::Unit::UI::GTK::TestRunner) comes into play. The console test runner is automatically invoked for you if you require ‘test/unit’ and simply run the file. To use another runner, or to manually invoke a runner, simply call its run class method and pass in an object that responds to the suite message with a Test::Unit::TestSuite. This can be as simple as passing in your TestCase class (which has a class suite method). It might look something like this:
   require 'test/unit/ui/console/testrunner'
   Test::Unit::UI::Console::TestRunner.run(TC_MyTest)

Test Suite

As more and more unit tests accumulate for a given project, it becomes a real drag running them one at a time, and it also introduces the potential to overlook a failing test because you forget to run it. Suddenly it becomes very handy that the TestRunners can take any object that returns a Test::Unit::TestSuite in response to a suite method. The TestSuite can, in turn, contain other TestSuites or individual tests (typically created by a TestCase). In other words, you can easily wrap up a group of TestCases and TestSuites like this:

 require 'test/unit/testsuite'
 require 'tc_myfirsttests'
 require 'tc_moretestsbyme'
 require 'ts_anothersetoftests'

 class TS_MyTests
   def self.suite
     suite = Test::Unit::TestSuite.new
     suite << TC_MyFirstTests.suite
     suite << TC_MoreTestsByMe.suite
     suite << TS_AnotherSetOfTests.suite
     return suite
   end
 end
 Test::Unit::UI::Console::TestRunner.run(TS_MyTests)

Now, this is a bit cumbersome, so Test::Unit does a little bit more for you, by wrapping these up automatically when you require ‘test/unit’. What does this mean? It means you could write the above test case like this instead:

 require 'test/unit'
 require 'tc_myfirsttests'
 require 'tc_moretestsbyme'
 require 'ts_anothersetoftests'

Test::Unit is smart enough to find all the test cases existing in the ObjectSpace and wrap them up into a suite for you. It then runs the dynamic suite using the console TestRunner.

To create a excellent large scale program, unit testing and debugging is a essential practice. As part of this process, the programmer needs to be familar with the termination and return codes and how to find the location and correct the syntax or other errors. Fortunately, these languages have extensive libraries and documentation sites that help with the development and testing process.

No comments:

Post a Comment