;; SUnit — minimal port written in SX-Smalltalk, run by smalltalk-load. ;; ;; Provides: ;; TestCase — base class. Subclass it, add `testSomething` methods. ;; TestSuite — a collection of TestCase instances; runs them all. ;; TestResult — passes / failures / errors counts and lists. ;; TestFailure — Error subclass raised by `assert:` and friends. ;; ;; Conventions: ;; - Test methods are run in a fresh instance per test. ;; - `setUp` is sent before each test; `tearDown` after. ;; - Failures are signalled by TestFailure; runner catches and records. (define st-sunit-source "Error subclass: #TestFailure instanceVariableNames: ''! Object subclass: #TestCase instanceVariableNames: 'testSelector'! !TestCase methodsFor: 'access'! testSelector ^ testSelector! testSelector: aSym testSelector := aSym. ^ self! ! !TestCase methodsFor: 'fixture'! setUp ^ self! tearDown ^ self! ! !TestCase methodsFor: 'asserts'! assert: aBoolean aBoolean ifFalse: [TestFailure signal: 'assertion failed']. ^ self! assert: aBoolean description: aString aBoolean ifFalse: [TestFailure signal: aString]. ^ self! assert: actual equals: expected actual = expected ifFalse: [ TestFailure signal: 'expected ' , expected printString , ' but got ' , actual printString]. ^ self! deny: aBoolean aBoolean ifTrue: [TestFailure signal: 'denial failed']. ^ self! should: aBlock raise: anExceptionClass | raised | raised := false. [aBlock value] on: anExceptionClass do: [:e | raised := true]. raised ifFalse: [ TestFailure signal: 'expected exception ' , anExceptionClass name , ' was not raised']. ^ self! shouldnt: aBlock raise: anExceptionClass | raised | raised := false. [aBlock value] on: anExceptionClass do: [:e | raised := true]. raised ifTrue: [ TestFailure signal: 'unexpected exception ' , anExceptionClass name]. ^ self! ! !TestCase methodsFor: 'running'! runCase self setUp. self perform: testSelector. self tearDown. ^ self! ! !TestCase class methodsFor: 'instantiation'! selector: aSym ^ self new testSelector: aSym! suiteForAll: aSelectorArray | suite | suite := TestSuite new init. suite name: self name. aSelectorArray do: [:s | suite addTest: (self selector: s)]. ^ suite! ! Object subclass: #TestResult instanceVariableNames: 'passes failures errors'! !TestResult methodsFor: 'init'! init passes := Array new: 0. failures := Array new: 0. errors := Array new: 0. ^ self! ! !TestResult methodsFor: 'access'! passes ^ passes! failures ^ failures! errors ^ errors! passCount ^ passes size! failureCount ^ failures size! errorCount ^ errors size! totalCount ^ passes size + failures size + errors size! addPass: aTest passes add: aTest. ^ self! addFailure: aTest message: aMsg | rec | rec := Array new: 2. rec at: 1 put: aTest. rec at: 2 put: aMsg. failures add: rec. ^ self! addError: aTest message: aMsg | rec | rec := Array new: 2. rec at: 1 put: aTest. rec at: 2 put: aMsg. errors add: rec. ^ self! isEmpty ^ self totalCount = 0! allPassed ^ (failures size + errors size) = 0! summary ^ 'Tests: {1} Passed: {2} Failed: {3} Errors: {4}' format: (Array with: self totalCount printString with: passes size printString with: failures size printString with: errors size printString)! ! Object subclass: #TestSuite instanceVariableNames: 'tests name'! !TestSuite methodsFor: 'init'! init tests := Array new: 0. name := 'Suite'. ^ self! name ^ name! name: aString name := aString. ^ self! ! !TestSuite methodsFor: 'tests'! tests ^ tests! addTest: aTest tests add: aTest. ^ self! addAll: aCollection aCollection do: [:t | self addTest: t]. ^ self! size ^ tests size! ! !TestSuite methodsFor: 'running'! run | result | result := TestResult new init. tests do: [:t | self runTest: t result: result]. ^ result! runTest: aTest result: aResult [aTest runCase. aResult addPass: aTest] on: TestFailure do: [:e | aResult addFailure: aTest message: e messageText]. ^ self! !") (smalltalk-load st-sunit-source)