7.4: SUnit by Example
- Page ID
- 36369
\( \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}\)Before going into the details of SUnit, we will show a step by step example. We use an example that tests the class Set. Try entering the code as we go along.
Step 1: create the test class
\(\bigstar\) First you should create a new subclass of TestCase called ExampleSetTest. Add two instance variables so that your new class looks like this:
Code \(\PageIndex{1}\) (Squeak): An Example Set Test Class
TestCase subclass: #ExampleSetTest instanceVariableNames: 'full empty' classInstanceVariableNames: '' poolDictionaries: '' category: 'MyTest'
We will use the class ExampleSetTest
to group all the tests related to the class Set
. It defines the context in which the tests will run. Here the context is described by the two instance variables full and empty that we will use to represent a full and an empty set.
The name of the class is not critical, but by convention it should end in Test
. If you define a class called Pattern
and call the corresponding test class PatternTest
, the two classes will be alphabetized together in the browser (assuming that they are in the same category). It is critical that your class be a subclass of TestCase
.
Step 2: initialize the test context
The method setUp
defines the context in which the tests will run, a bit like an initialize method. setUp
is invoked before the execution of each test method defined in the test class.
\(\bigstar\) Define the setUp method as follows, to initialize the empty variable to refer to an empty set and the full variable to refer to a set containing two elements.
Code \(\PageIndex{2}\) (Squeak): Setting Up a Fixture
ExampleSetTest»setUp empty := Set new. full := Set with: 5 with: 6
In testing jargon the context is called the fixture for the test.
Step 3: write some test methods
Let’s create some tests by defining some methods in the class ExampleSetTest
. Each method represents one test; the name of the method should start with the string ‘test
’ so that SUnit will collect them into test suites. Test methods take no arguments.
\(\bigstar\) Define the following test methods.
The first test, named testIncludes
, tests the includes: method of Set
. The test says that sending the message includes: 5
to a set containing 5 should return true
. Clearly, this test relies on the fact that the setUp
method has already run.
Code \(\PageIndex{3}\) (Squeak): Testing Set Membership
ExampleSetTest»testIncludes self assert: (full includes: 5). self assert: (full includes: 6)
The second test, named testOccurrences
, verifies that the number of occurrences of 5 in full
set is equal to one, even if we add another element 5 to the set.
Code \(\PageIndex{4}\) (Squeak): Testing Occurrences
ExampleSetTest»testOccurrences self assert: (empty occurrencesOf: 0) = 0. self assert: (full occurrencesOf: 5) = 1. full add: 5. self assert: (full occurrencesOf: 5) = 1
Finally, we test that the set no longer contains the element 5 after we have removed it.
Code \(\PageIndex{5}\) (Squeak): Testing Removal
ExampleSetTest»testRemove full remove: 5. self assert: (full includes: 6). self deny: (full includes: 5)
Note the use of the method deny:
to assert something that should not be true. aTest deny: anExpression
is equivalent to aTest assert: anExpression
not, but is much more readable.
Step 4: run the tests
The easiest way to execute the tests is with the SUnit Test Runner, which you can open from the World ⊳ open. . .
menu, or by dragging the TestRunner
from the Tools flap. The TestRunner, shown in Figure \(\PageIndex{1}\), is designed to make it easy to execute groups of tests. The left-most pane lists all of the system categories that contain test classes (i.e., subclasses of TestCase
); when some of these categories are selected, the test classes that they contain appear in the pane to the right. Abstract classes are italicized, and the test class hierarchy is shown by indentation, so subclasses of ClassTestCase
are indented more than subclasses of TestCase
.
\(\bigstar\) Open a Test Runner, select the category MyTest, and click the Run Selected button.
You can also run your test by executing a print it
on the following code: (ExampleSetTest selector: #testRemove) run
. This expression is equivalent to the shorter ExampleSetTest run: #testRemove
. We usually include an executable comment in our test methods that allows us to run them with a do it from the browser, as shown in Code \(\PageIndex{6}\).
Code \(\PageIndex{6}\) (Squeak): Executable Comments in Test Methods
ExampleSetTest»testRemove "self run: #testRemove" full remove: 5. self assert: (full includes: 6). self deny: (full includes: 5)
\(\bigstar\) Introduce a bug in ExampleSetTest»testRemove and run the tests again. For example, change 5 to 4.
The tests that did not pass (if any) are listed in the right-hand panes of the Test Runner; if you want to debug one, to see why it failed, just click on the name. Alternatively, you can execute the following expressions:
(ExampleSetTest selector: #testRemove) debug
or
ExampleSetTest debug: #testRemove
Step 5: interpret the results
The method assert:
, which is defined in the class TestCase
, expects a boolean argument, usually the value of a tested expression. When the argument is true, the test passes; when the argument is false, the test fails.
There are actually three possible outcomes of a test. The outcome that we hope for is that all of the assertions in the test are true, in which case the test passes. In the test runner, when all of the tests pass, the bar at the top turns green. However, there are also two kinds of thing that can go wrong when you run a test. Most obviously, one of the assertions can be false, causing the test to fail. However, it is also possible that some kind of error occurs during the execution of the test, such as a message not understood error or an index out of bounds error. If an error occurs, the assertions in the test method may not have been executed at all, so we can’t say that the test has failed; nevertheless, something is clearly wrong! In the test runner, failing tests cause the bar at the top to turn yellow, and are listed in the middle pane on the right, whereas erroneous tests cause the bar to turn red, and are listed in the bottom pane on the right.
\(\bigstar\) Modify your tests to provoke both errors and failures.