Using Squeak's SUnit to test your code


We tested our new code manually. There's a tool built in to Squeak called SUnit which allows for repeatable test execution. If we add test code to SUnit to check our new filename copy method we could assure that any time someone makes changes to the FileList class in the future, if they run SUnit, they will know if they did something causing our code to break.

In the world of eXtreme Programming (XP) the rule is that you write the SUnit test case code before you implement your model or solution code. The idea is that by thinking about how the test should work you will better understand how the code should be designed. We didn't do that in this case however. But we will later in the example.

The first check we should make is to actually exercise SUnit. The idea is that the running of all known test cases should not produce any test failures. Once we are certain we can introduce our new test. In general, we would actually run SUnit tests before we introduce a single line of code in FileList, and verify that all tests pass. Then, if there are sufficient tests for FileList, when we run SUnit again after we add code we might expect something to break in a test. That's assuming the changes we make would be caught by an existing test case. Regardless, let's take a moment and review how to run SUnit tests in Squeak.

In Squeak 3.5 you launch SUnit by opening the Test Runner from the "open..." menu.


You will see the Test Runner window open.

Click on the "Run All" button. When it has completed running all the SUnit tests you will see a summary.

171 tests were run and they all passed. There's a sign of real progress here from back when this tutorial was written for Squeak 3.2. Back then there were 118 tests. By the way, if you get any errors reported by SUnit, it's possible there may be platform specific bugs or some other kind of bug in your system. For our purposes don't get too distracted by any bugs that show up here in classes we are not coding.

The next check we should make is to see if there already is a FileList test class in SUnit. There is! Again, back when the tutorial was written to use Squeak 3.2 there was no pre-existing FileList test case. If there had not been, we would have made a new subclass of TestCase and continued.

Let's add a test for our new method to the test case. Using a standard class Browser we perform a "find class..." on FileListTest. Choose the "test" method protocol to review the existing test methods.

I'd like to exercise FileList and validate that the original "copy name to clipboard" menu operation and the new "copy just file name to clipboard" menu operation work as expected. Since we cannot expect the environment SUnit is running in to have any specific file we can use for testing, we'll have to create one.

After we create the temporary file we will use just for the tests, we'll perform some operations on it, verify expected results, and then delete the file.

I wrote a few methods to support our intent to test this way. Both these two methods shown below are included in the method cateogry "private" on our test class. Here are the two new methods:

tempFileContents
    ^ 'test file contents'

createTempFile
    | dir time fname fstream |
    dir := FileDirectory default.
    time := Time now.
    fname := time hhmm24 , time seconds printString , '.tmp'.
    (dir fileExists: fname)
        ifTrue: [dir deleteFileNamed: fname].
    fstream := dir forceNewFileNamed: fname.
    fstream nextPutAll: self tempFileContents.
    fstream close.
    ^ dir -> fname
The #createTempFile method answers an Association containing the location and actual file name of the temporary file we create. Since we will be using a GUI list and menu to test our new code we'll have to write a little bit of support code to make it easy to interact with the FileList GUI.
fileListMorph: gui
    ^ ((gui allMorphs
        select: [:m | m isKindOf: PluggableListMorph])
            select: [:m | m getListSelector = #fileList]) first
Now we can create the test code. SUnit will exercise all methods in subclasses of TestCase that begin with "test" in the selector name. Here's the test code. I put it in the "test" method category.
testFileNameOperations
     | gui assoc dir fname entry index listMorph string |
     Smalltalk isMorphic
         ifFalse: [^ nil].
     assoc := self createTempFile.
     dir := assoc key.
     fname := assoc value.
     gui := FileList openAsMorph.
     "Since we just opened up the FileList we can assume it's directory
      is the same as ours."
     entry := gui model fileList
         detect: [:each | each includesSubString: fname]
         ifNone: [].
     index := gui model fileList indexOf: entry.
     listMorph := self fileListMorph: gui.
     listMorph selectionIndex: index.
     gui model fileListIndex: index.
     "Now that we faked the selection of our test file, ask our FileList
      to copy the name."
     gui model copyName.
     string := Clipboard clipboardText string.
     self assert: string = (dir fullNameFor: fname).
     gui model copyJustFileName.
     string := Clipboard clipboardText string.
     self assert: string = fname.
     "Done with the file."
     dir deleteFileNamed: fname.
     gui delete
There's a bunch of little "tricks" going on in here. First we got lucky because the FileList #openAsMorph class method answers a SystemWindow but doesn't actually open it. So we can use that window morph and ask it questions about it's submorphs.

Also, once we had all the hooks in place to test the new #copyJustFileName method we wrote, we may as well test the existing one that answered the full path: #copyName also. The #assert: methods will generate an error that SUnit will log if the condition we assert is not true.

Run the SUnit tests again after adding this method.


Now there are 172 tests and they all pass! You might pause to consider that a smarter way to use SUnit is to actually write a test that you will know will fail when the method is not written yet. Then you would write the actual method on the model (FileList) that you want to test and see SUnit report that it worked.

Close The Test Runner.

Go on to Second Enhancement to File List.

Back to the beginning of this example.