Mikhail Gorshenev's blog
Archives
« November 2009
SunMonTueWedThuFriSat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
     
       
Today
Click me to subscribe
Search

Links
 

Today's Page Hits: 16

« JavaOne 2007, Part... | Main | cq3G release »
Tuesday May 15, 2007
Use of Exceptions in Tests

What is the best way to return status from a test case? This question has been asked more than once during the last week so it deserves a blog post.

Different test frameworks handle test results differently. Alexey has simple a side-to-side comparison of tests formats for JUnit-like tests and JT Harness in his blog. JUnit uses exceptions to signal test failure, JT Harness returns a Status object:

JUnit

    public void test() throws AssertionFailedException {
        // do something 
        ...
        // check result 
        assertEquals(expectedResult, result);
    }

JT Harness

    public Status test() {
        // do something 
        ...
        // check result 
        return (expectedResult == result) 
                ? Status.passed("OK") 
                : Status.failed("Unexpected result " + result);
    }

The exception-throwing approach appears to be the most straightforward, but it has its drawbacks. While for the simplest unit tests it may make the code easier to read, it quickly gets difficult in more complex cases. And exceptions affect test performance as well.

Here are a few other reasons to use the Status object.

First, use of exception to signal test status violates one of the important API design principles: "never use exceptions for flow control". Exception use is normally restricted to recovery from abnormal conditions such as resource failures or programming errors. A common argument is that test failure is caused by programming error and thus use of an exception is justified, but this is indeed not the case. The one and only reason to write unit tests is to detect bugs. While there is a bug in the underlying code, it should be considered normal flow of control for the test. On the other hand, if the test itself encounters a problem or the underlying code break down unexpectedly, then it is appropriate to throw an exception.

Second, use of an exception artificially limits test outcomes to just "Pass" or "Fail". Use of a return type allows to easily extend test format with additional status types. We found it convenient to have at least yet another status, "Error". "Error" is different from "Fail" as it signals a problem with test initialization or execution; "Fail" means that the test executed successfully and found a bug in the code. Throwing an exception in case of a test initialization problem is justified. In fact, JT Harness defines an exception class (MultiTest.SetupException) just for this purpose.

To summarize, use of a Status object is a cleaner API design. But if you like the brevity of the exception mechanism, it is fine to use as well. Just don't use both at the same time :).

Posted at 03:56PM May 15, 2007 by Mikhail Gorshenev in Sun  |  Comments[0]

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed