PackageDescription: Seaside-SUnitToo


Seaside - S Unit Too

Last published: April 21, 2009 by 'mkobetic'

Defines 4 Classes
Extends 7 Classes


A framework on top of SUnitToo for unit testing of Seaside components/tasks in the same manner as they run on the web. TestCase is automatically configured with a preinitialized component and a UserAgent representing a browser accessing the component. During test #setUp the component gets registred with the Seaside server, and the agent hits the first page of the component. Therefore by the time the test method starts the first page is already available for inspection. A Page captures not just the response, but also its URL, the parsed DOM tree of the contents and a copy of the component (from the corresponding continuation) in the state that produced the page. XPath can be used to analyze and validate the parsed response contents (see Page>>find:).

Note: The most convenient way to run SUnitToo test suites is using SUnitToo(ls) package.

Let's build a simple test case for the WACounter demo. We need to create a subclass of SeasideSUnitToo.TestCase, e.g. WACounterTest and specify the class of the component to test.

WACounterTest>>componentClass
^WACounter

And here's an example test checking the function of the ++ link:

testIncrease
self assert: self component count isZero. "check current state of the component"
self clickLinkWithText: '++'. "find a link with text '++' on current page and simulate a click, i.e. do a GET of the associated href"
self assert: self component count = 1. "check current value of the counter"
self assert: (self find: '//h1/text()') first text = '1' "find the heading element on current page and make sure the text corresponds to current counter value"

The agent caches returned pages the same way a web browser would. It allows to look #back and #forward in the page history. It works the same way as web browser back/forward buttons, so #currentPage doesn't have to be the last one in the history. Here's another sample test exercising the continuation magic in the face of the back button.

testClickBackClick
self assert: self component count = 0. "initial state"
self clickLinkWithText: '++'. "hit the increment link"
self assert: self component count = 1. "check new state"
self back. "click the back button"
self assert: (self find: '//h1/text()') first text = '0'. "current page should display 0"
self assert: self pageComponent count = 0. "the snapshot of the component when it generated current page should have count 0"
self clickLinkWithText: '++'. "click incremement"
self assert: self component count = 1. "check new state of the component"

Since both the client and the server live in the same image the agent also provides access to all interesting participants of the test: the instantiated component, associated application and session and also the HttpClient instance used to hit the server. The server used for testing is the global 'SeasideServer class>>current'. Tested components get automatically registered with the server during test setUp (under the #componentTestingPath) and unregistred in test tearDown.

Finally, since most non-trivial web applications will likely use form posts, there's also the HtmlForm. It provides a number of helper methods emulating various types of form input.

testCheckBox
self newForm
check: 'id_a'; "set the check box value"
check: 'id_c'; "set the check box value"
click: 'Submit'. "click the submit button"

testRadioButton
self newForm
radioCheck: 'id1_a'; "set the radio button value" "
click: 'Submit'. "click the submit button"

testDisplay
(self newForm)
fill: 'Bob Smith' in: 'name'; "fill in an input field"
fill: '16' in: 'age'; "fill in an input field"
click: 'Display'. "click the submit button"

testSelection
self newForm
select: #( 'Dakar' 'Sydney' ) in: 'multiSelectionList'; "select in a multi-selection list"
click: 'Submit'. "click the submit button"

The #click: also performs the form post, so it needs to be last in the sequence. If a form doesn't have a submit button, HtmlForm>>post can be called directly as well. It is also possible to emulate file upload. Upload posts automatically as well.

testFileUpload
self newForm upload: 'test.txt' in: 'upload' source: 'Hello World!' readStream.