7.8: The Implementation of SUnit
- Page ID
- 38019
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
.

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