9.6: The SUnit Framework
- Page ID
- 39624
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)SUnit consists of four main classes: TestCase
, TestSuite
, TestResult
, and TestResource
, as shown in Figure \(\PageIndex{1}\). The notion of a test resource represents a resource that is expensive to set-up but which can be used by a whole series of tests. A TestResource
specifies a setUp
method that is executed just once before a suite of tests; this is in distinction to the TestCase >> setUp
method, which is executed before each test.

TestCase
TestCase
is an abstract class that is designed to be subclassed. Each of its subclasses represents a group of tests that share a common context (that is, a test suite). Each test is run by creating a new instance of a subclass of TestCase
, running setUp
, running the test method itself, and then sending the tearDown
.
The context is specified by instance variables of the subclass and by the specialization of the method setUp
, which initializes those instance variables. Subclasses of TestCase
can also override method tearDown
, which is invoked after the execution of each test, and can be used to release any objects allocated during setUp
.
TestSuite
Instances of the class TestSuite
contain a collection of test cases. An instance of TestSuite
contains tests, and other test suites. That is, a test suite contains sub-instances of TestCase
and TestSuite
.
Both individual test cases and test suites understand the same protocol, so they can be treated in the same way (for example, both can be run). This is in fact an application of the Composite
pattern in which TestSuite
is the composite and the test cases are the leaves.
TestResult
The class TestResult
represents the results of a TestSuite
execution. It records the number of tests passed, the number of tests failed, and the number of errors signalled.
TestResource
One of the important features of a suite of tests is that they should be independent of each other. The failure of one test should not cause an avalanche of failures of other tests that depend upon it, nor should the order in which the tests are run matter. Performing setUp
before each test and tearDown
afterwards helps to reinforce this independence.
However, there are occasions where setting up the necessary context is just too time-consuming for it to be done before the execution of each test. Moreover, if it is known that the test cases do not disrupt the resources used by the tests, then it is wasteful to set them up afresh for each test. It is sufficient to set them up once for each suite of tests. Suppose, for example, that a suite of tests needs to query a database, or do analysis on some compiled code. In such cases, it may make sense to set up the database and open a connection to it, or to compile some source code, before any of the tests start to run.
Where should we cache these resources, so that they can be shared by a suite of tests? The instance variables of a particular TestCase
subclass won’t do, because a TestCase instance persists only for the duration of a single test (as mentioned before, the instance is created anew for each test method). A global variable would work, but using too many global variables pollutes the name space, and the binding between the global and the tests that depend on it will not be explicit. A better solution is to put the necessary resources in a singleton object of some class. The class TestResource
exists to be subclassed by such resource classes. Each subclass of TestResource
understands the message current
, which will answer a singleton instance of that subclass. Methods setUp
and tearDown
should be overridden in the subclass to ensure that the resource is initialized and finalized.
One thing remains: somehow, SUnit has to be told which resources are associ- ated with which test suite. A resource is associated with a particular subclass of TestCase
by overriding the class method resources
.
By default, the resources of a TestSuite
are the union of the resources of the TestCases
that it contains.
Here is an example. We define a subclass of TestResource
called MyTestResource
. Then we associate it with MyTestCase
by overriding the class method MyTestCase class >> resources
to return an array of the test resource classes that MyTestCase
will use.
TestResource subclass: #MyTestResource instanceVariableNames: '' ... MyTestCase class >> resources "Associate the resource with this class of test cases" ^ { MyTestResource }
Exercise
The following trace (written to the Transcript
) illustrates that a global set up is run before and after each test in a sequence. Let’s see if you can obtain this trace yourself.
MyTestResource >> setUp has run. MyTestCase >> setUp has run. MyTestCase >> testOne has run. MyTestCase >> tearDown has run. MyTestCase >> setUp has run. MyTestCase >> testTwo has run. MyTestCase >> tearDown has run. MyTestResource >> tearDown has run.
Create new classes MyTestResource
and MyTestCase
which are subclasses of TestResource
and TestCase
respectively. Add the appropriate methods so that the following messages are written to the Transcript
when you run your tests.
- Solution.
-
You will need to write the following six methods.
MyTestCase >> setUp Transcript show: 'MyTestCase>>setUp has run.'; cr MyTestCase >> tearDown Transcript show: 'MyTestCase>>tearDown has run.'; cr MyTestCase >> testOne Transcript show: 'MyTestCase>>testOne has run.'; cr MyTestCase >> testTwo Transcript show: 'MyTestCase>>testTwo has run.'; cr MyTestCase class >> resources ^ Array with: MyTestResource MyTestResource >> setUp Transcript show: 'MyTestResource>>setUp has run'; cr MyTestResource >> tearDown Transcript show: 'MyTestResource>>tearDown has run.'; cr