Skip to main content
Engineering LibreTexts

7.8: The Implementation of SUnit

  • Page ID
    38019
  • \( \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}}\)

    The implementation of SUnit makes an interesting case study of a Smalltalk framework. Let’s look at some key aspects of the implementation by following the execution of a test.

    Running one test

    To execute one test, we evaluate the expression (aTestClass selector: aSymbol) run.

    Running one test.
    Figure \(\PageIndex{1}\): Running one test.

    The method TestCase»run creates an instance of TestResult that will accumulate the results of the tests, then it sends itself the message run:. (See Figure \(\PageIndex{1}\).)

    Code \(\PageIndex{1}\) (Squeak): Running a Test Case

    TestCase»run
        | result |
        result := TestResult new.
        self run: result.
        ↑result
    

    The method TestCase»run: sends the message runCase: to the test result:

    Code \(\PageIndex{2}\) (Squeak): Passing the Test Case to the Test Result

    TestCase»run: aResult
        aResult runCase: self
    

    The method TestResult»runCase: sends the message runCase to an individual test, to execute the test. TestResult»runCase deals with any exceptions that may be raised during the execution of a test, runs a TestCase by sending it the message runCase, and counts the errors, failures and passes.

    Code \(\PageIndex{3}\) (Squeak): Catching Test Case Errors and Failures

    TestResult»runCase: aTestCase
        | testCasePassed |
        testCasePassed := true.
        [[aTestCase runCase]
            on: self class failure
            do:
                [:signal |
                failures add: aTestCase.
                testCasePassed := false.
                signal return: false]]
                    on: self class error
                    do:
                        [:signal |
                        errors add: aTestCase.
                        testCasePassed := false.
                        signal return: false].
        testCasePassed ifTrue: [passed add: aTestCase]
    

    The method TestCase»runCase sends the messages setUp and tearDown as shown below.

    Code \(\PageIndex{4}\) (Squeak): Test Case Template Method

    TestCase»runCase
        self setUp.
        [self performTest] ensure: [self tearDown]
    

    Running a TestSuite

    To run more than one test, we send the message run to a TestSuite that contains the relevant tests. TestCase class provides some functionality to build a test suite from its methods. The expression MyTestCase buildSuiteFromSelectors returns a suite containing all the tests defined in the MyTestCase class. The core of this process is

    Code \(\PageIndex{5}\) (Squeak): Auto-Building the Test Suite

    TestCase class»testSelectors
        ↑self selectors asSortedCollection asOrderedCollection select: [:each |
        ('test*' match: each) and: [each numArgs isZero]]
    

    The method TestSuite»run creates an instance of TestResult, verifies that all the resources are available, and then sends itself the message run:, which runs all the tests in the suite. All the resources are then released.

    Code \(\PageIndex{6}\) (Squeak): Running a Test Suite

    TestSuite»run
        | result |
        result := TestResult new.
        self areAllResourcesAvailable
            ifFalse: [↑TestResult signalErrorWith:
                'Resource could not be initialized'].
        [self run: result] ensure: [self resources do:
            [:each | each reset]].
        ↑result
    

    Code \(\PageIndex{7}\) (Squeak): Passing the Test Suite to the Test Result

    TestSuite»run: aResult
        self tests do:
            [:each |
                self sunitChanged: each.
                each run: aResult]
    

    The class TestResource and its subclasses keep track of their currently created instances (one per class) that can be accessed and created using the class method current. This instance is cleared when the tests have finished running and the resources are reset.

    The resource availability check makes it possible for the resource to be re-created if needed, as shown in the class method TestResource class»isAvailable. During the TestResource instance creation, it is initialized and the method setUp is invoked.

    Code \(\PageIndex{8}\) (Squeak): Test Resource Availability

    TestResource class»isAvailable
        ↑self current notNil
    

    Code \(\PageIndex{9}\) (Squeak): Test Resource Creation

    TestResource class»current
        current isNil ifTrue: [current := self new].
        ↑current
    

    Code \(\PageIndex{10}\) (Squeak): Test Resource Initialization

    TestResource»initialize
        self setUp
    

    This page titled 7.8: The Implementation of SUnit 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.