CS 535 Object-Oriented Programming Fall Semester, 2003 Testing, Debugging, Object |
||
---|---|---|
© 2003, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 22-Sep-03 |
Testing
Johnson's Law
If it is not tested it does not work
Types of tests
Why Unit Testing
If it is not tested it does not work
The more time between coding and testing
Unit Tests Must be Easy To Run
Must be able to
Testing First
First write the tests
Then write the code to be tested
Writing tests first:
SUnit
Testing framework for automating running of unit tests in Smalltalk
In SUnit
Two GUI Interfaces for viewing Test Results
Loading Browser SUnit Extensions
Open the Parcel Manager by selecting the “Parcel Manager” item in the “System” menu of the launcher.
In the parcel manager:
How to Use SUnit
We will test the following class
Smalltalk defineClass: #Counter superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'count ' classInstanceVariableNames: '' imports: '' category: 'Course-Examples'
Class method new ^super new initialize
Instance Methods count ^count
decrease self increment: 1 "Note the error"
increase self increment: 1
increment: anInteger self halt. count := count + anInteger
initialize count := 0
Creating and Using Unit Tests
1. Make test class a subclass of TestCase
Smalltalk defineClass: #TestCounter superclass: #{XProgramming.SUnit.TestCase} indexedType: #none private: false instanceVariableNames: '' classInstanceVariableNames: '' imports: '' category: 'Course-Examples'
You do not have to remember the full name of TestCase. Just use the name TestCase. The browser will expand it to the full name for you.
2. Make test methods
testIncrease | aCounter | aCounter := Counter new. self assert: aCounter count = 0. aCounter increase. self assert: aCounter count = 1.
TestCase methods of interest
Methods to assert conditions:
assert: aBooleanExpression deny: aBooleanExpression should: [aBooleanExpression] should: [aBooleanExpression] raise: AnExceptionClass shouldnt: [aBooleanExpression] shouldnt: [aBooleanExpression] raise: AnExceptionClass signalFailure: aString setUp
tearDown
Running the Test using Browser SUnit Extensions
If you have loaded RBSUintExtensions you will see a Run button below the code pane in any method whose selector starts with “test” in a subclass of TestCase. Just click on the run button to run the test. It the test passes you will get a green bar below the code pane, if it fails you will get a red bar. If you get a red bar you can click on the “Debug” button to run the test in the debugger. When you have more tests, you can select just the class and the run button will run all the tests in the class. If the test(s) pass you will get a green bar as shown below.
A Failed Test
When a test fails you get a red bar.
How to Use TestRunner
TestRunner open
More Test Examples to Show Syntax
Smalltalk.CS535 defineClass: #TestCounter superclass: #{XProgramming.SUnit.TestCase} indexedType: #none private: false instanceVariableNames: 'counter ' classInstanceVariableNames: '' imports: '' category: 'Course-Examples'
CS535.TestCounter methodsFor: 'testing'
setUp counter := Counter new.
tearDown counter := nil.
testDecrease counter decrease. self assert: counter count = -1.
testDecreaseWithShould "Just an example to show should: syntax" counter decrease. self should: [counter count = -1].
testIncrease self deny: counter isNil. counter increase. self assert: counter count = 1.
testZeroDivide "Just an example to show should:raise: syntax" self should: [1/0] raise: ZeroDivide. self shouldnt: [1/2] raise: ZeroDivide
Debugging
Ways the debugg is called
When we run our testDecrease method it fails and shows a red bar.
If one clicks on the Debug button you get the standard exception window.
When you click on the debug button in the exception window, you get the debugger. The debugger looks like:
Debugger Components Execution Stack Pane
Self halt Example
The statement "self halt." opens the exception window when executed, which allows one to enter the debugger.
Smalltalk defineClass: #Counter superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'count ' classInstanceVariableNames: '' imports: '' category: 'Course-Examples'
Class method new ^super new initialize
Instance Methods count ^count
decrease self increment: 1 "Note the error"
increase self increment: 1
increment: anInteger self halt. count := count + anInteger
initialize count := 0
Sample Test code in Workspace
Execute the following code with "Do it"
| count | count := Counter new. count decrease; decrease; increase.
When the statement "self halt" is executed you will see the exception window.
Object
All 'things' in Smalltalk are objects
Objects are created from classes
The class Object is the parent class of all classes
Object class contains common methods for all objects
Determines behavior for all objects
Some Important Behavior Defined in Object
printString
| a | |
Result printed |
Transcript |
|
clear; |
|
print: a isNil; |
true |
cr; |
|
show: a printString; |
'nil' |
cr; |
|
print: a class; |
UndefinedObject |
cr. |
|
a := 5. |
|
Transcript |
|
print: a isNil; |
false |
cr; |
|
show: a printString; |
5 |
cr; |
|
print: a |
5 |
cr; |
|
printString
Since implemented in Object all objects inherit the method
printString is sent to an object when
Overriding Default printString Behavior
Implement printOn: not printString
printOn:'s argument is a stream
Stream Writing methods:
Example - Counter
Add the following method to the Counter class
Counter>>printOn: aStream aStream nextPutAll: 'Counter('; print: count; nextPutAll: ')'
Now execute the following with a "Print it"
| test | test := Counter new. test increase. ^test
Now instead of printing “a Counter” you will get “Counter(1)”
Who does printOn: change printString?
printString uses printOn:
Object>>printString "Answer a String whose characters are a description of the receiver." | aStream | aStream := WriteStream on: (String new: 16). self printOn: aStream. ^aStream contents
Equality
All objects are allocated on the heap
Variables are references (like a pointer) to objects
A == B
Returns true if the two variables point to the same location
A = B
Returns true if the two variables point to equivalent objects
In Smalltalk you want to use '=' nearly all the time
A ~= B
Means (A = B) not
A ~~ B
Means (A == B) not
Equality Example - Counter
| a b |
a := SimpleCircle origin: (0 @ 0) radius: 1.
b := a.
Now a == b and a= b are both true
a := SimpleCircle origin: (0 @ 0) radius: 1.
b := SimpleCircle origin: (2 @ 5) radius: 3.
Now a == b and a= b are both false
a := SimpleCircle origin: (0 @ 0) radius: 1.
b := SimpleCircle origin: (0 @ 0) radius: 1..
Now a == b is false
Should a = b be true or false?
Defining the Meaning of =
Object
Does not know what ='s means for your class
Default definition is to use ==
If you override = also override #hash
Copyright ©, All rights reserved.
2003 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 22-Sep-03    Next