Skip to main content
Engineering LibreTexts

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}}\)

    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.

    The Squeak SUnit Test Runner.
    Figure \(\PageIndex{1}\): The Squeak SUnit Test Runner.

    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.


    This page titled 7.4: SUnit by Example is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Andrew P. Black, Stéphane Ducasse, Oscar Nierstrasz, Damien Pollet via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.