CS 5010 Program Design Paradigms "Bootcamp" Lesson 9.4

Slides:



Advertisements
Similar presentations
Testing Simple Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson 10.6 © Mitchell Wand, This work is licensed under a Creative Commons.
Advertisements

Classes, Objects, and Methods CS 5010 Program Design Paradigms "Bootcamp" Lesson 10.1 © Mitchell Wand, This work is licensed under a Creative.
Language Issues for Inheritance CS 5010 Program Design Paradigms "Bootcamp" Lesson 12.3 © Mitchell Wand, This work is licensed under a Creative.
Lists CS 5010 Program Design Paradigms “Bootcamp” Lesson 4.1 TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: AAA 1 ©
Structural Decomposition CS 5010 Program Design Paradigms “Bootcamp” Lesson 2.1 © Mitchell Wand, This work is licensed under a Creative Commons.
Introducing General Recursion CS 5010 Program Design Paradigms “Bootcamp” Lesson 8.2 TexPoint fonts used in EMF. Read the TexPoint manual before you delete.
Patterns of Communication Between Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson 11.1 © Mitchell Wand, This work is licensed under.
Converting from Immutable to Mutable Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson 11.4 © Mitchell Wand, This work is licensed under.
The Design Recipe using Classes CS 5010 Program Design Paradigms "Bootcamp" Lesson 10.5 © Mitchell Wand, This work is licensed under a Creative.
Design Strategies 2: Using a template CS 5010 Program Design Paradigms “Bootcamp” Lesson 2.1 © Mitchell Wand, This work is licensed under a Creative.
Rewriting your function using map and foldr CS 5010 Program Design Paradigms “Bootcamp” Lesson TexPoint fonts used in EMF. Read the TexPoint manual.
More examples of invariants CS 5010 Program Design Paradigms “Bootcamp” Lesson © Mitchell Wand, This work is licensed under a Creative.
Generalizing Over Functions CS 5010 Program Design Paradigms “Bootcamp” Lesson TexPoint fonts used in EMF. Read the TexPoint manual before you delete.
A Case Study: Space Invaders CS 5010 Program Design Paradigms "Bootcamp" Lesson © Mitchell Wand, This work is licensed under a Creative.
Testing Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson © Mitchell Wand, This work is licensed under a Creative Commons Attribution-NonCommercial.
The Design Recipe using Classes CS 5010 Program Design Paradigms "Bootcamp" Lesson © Mitchell Wand, This work is licensed under a Creative.
Callbacks and Interacting Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson © Mitchell Wand, This work is licensed under a Creative.
Halting Measures and Termination Arguments CS 5010 Program Design Paradigms “Bootcamp” Lesson TexPoint fonts used in EMF. Read the TexPoint manual.
Observerables are not Fields CS 5010 Program Design Paradigms "Bootcamp" Lesson 10.7 © Mitchell Wand, This work is licensed under a Creative.
Solving Your Problem by Generalization CS 5010 Program Design Paradigms “Bootcamp” Lesson © Mitchell Wand, This work is licensed under.
Converting from Immutable to Mutable Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson © Mitchell Wand, This work is licensed.
Classes, Objects, and Interfaces CS 5010 Program Design Paradigms "Bootcamp" Lesson © Mitchell Wand, This work is licensed under a Creative.
How to use an Observer Template
CS 5010 Program Design Paradigms “Bootcamp” Lesson 2.4
Contracts, Purpose Statements, Examples and Tests
CS 5010 Program Design Paradigms “Bootcamp” Lesson 2.2
The Point of This Course
Generalizing Similar Functions
CS 5010 Program Design Paradigms “Bootcamp” Lesson 5.2
Examining Two Pieces of Data
CS 5010 Program Design Paradigms “Bootcamp” Lesson 5.4
More About Recursive Data Types
CS 5010 Program Design Paradigms “Bootcamp” Lesson 3.1
CS 5010 Program Design Paradigms “Bootcamp” Lesson 4.3
CS 5010 Program Design Paradigms “Bootcamp” Lesson 1.3
Reviewing your Program
More Recursive Data Types
CS 5010 Program Design Paradigms “Bootcamp” Lesson 8.7
Halting Functions for Tree-Like Structures
CS 5010 Program Design Paradigms “Bootcamp” Lesson 4.1
Generalizing this Design
CS 5010 Program Design Paradigms “Bootcamp” Lesson 5.3
CS 5010 Program Design Paradigms "Bootcamp" Lesson 12.1
CS 5010 Program Design Paradigms “Bootcamp” Lesson 1.3
CS 5010 Program Design Paradigms “Bootcamp” Lesson 5.1
From Templates to Folds
Callbacks and Interacting Objects
CS 5010 Program Design Paradigms "Bootcamp" Lesson 9.3
Patterns of Interaction 2: Publish-Subscribe
Case Study: Undefined Variables
Patterns of Interaction 2: Publish-Subscribe
CS 5010 Program Design Paradigms “Bootcamp” Lesson 7.5
Solving Your Problem by Generalization
A Case Study: Space Invaders
Testing Mutable Objects
Contracts, Purpose Statements, Examples and Tests
CS 5010 Program Design Paradigms “Bootcamp” Lesson 6.5
More examples of invariants
ormap, andmap, and filter
Generalizing Similar Functions
The Iterative Design Recipe
CS 5010 Program Design Paradigms "Bootcamp" Lesson 12.1
Rewriting your function using map and foldr
Examining Two Pieces of Data
A Case Study: Space Invaders
CS 5010 Program Design Paradigms “Bootcamp” Lesson 4.1
When do I need an invariant?
CS 5010 Program Design Paradigms “Bootcamp” Lesson 8.3
Presentation transcript:

CS 5010 Program Design Paradigms "Bootcamp" Lesson 9.4 Testing Objects CS 5010 Program Design Paradigms "Bootcamp" Lesson 9.4 © Mitchell Wand, 2012-2016 This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Key Points of this lesson We can only test observable behavior observables are not fields fields are not observable in general We need to decide what properties are important, and test those. we can’t use equal?

An example Let’s consider a really simple interface:

StupidRobot<%> ;; A StupidRobot is an object of any class that implements ;; StupidRobot<%> ;; Interpretation: A StupidRobot represents a robot moving along a ;; one-dimensional line, starting at position 0. (define StupidRobot<%> (interface () ;; a new StupidRobot is required to start at position 0 ;; -> StupidRobot ;; RETURNS: a Robot just like this one, except moved one ;; position to the right move-right ;; -> Integer ;; RETURNS: the current x-position of this robot get-pos )) All StupidRobots must obey this additional condition (an invariant!) so we mention it here in the interface The only observable is the position of the robot.

Scenario and Observation If we have a correct implementation of StupidRobot<%>, the following test should pass: (local ((define r0 ..a new StupidRobot<%>..) ;; move r0 right twice (define r1 (send (send r0 move-right) move-right))) ;; get-pos should then return 2 (check-equal (send r1 get-pos) 2))

The "obvious" implementation ;; Constructor Template for Robot1% ;; (new Robot1% [x NonNegInt]) ;; x is optional; default is 0 (define Robot1% (class* object% (StupidRobot<%>) (init-field [x 0]) ;; interp: the position of the robot. (super-new) (define/public (move-right) (new Robot1% [x (+ x 1)])) (define/public (get-pos) x) )) Of course, our choice of x for the field name is arbitrary.

You could name fields anything you want ;; Constructor Template for Robot2% ;; (new Robot2% [blerch NonNegInt]) ;; blerch is optional; default is 0 (define Robot2% (class* object% (StupidRobot<%>) (init-field [blerch 0]) ;; interp: the position of the robot. (super-new) (define/public (move-right) (new Robot2% [blerch (+ blerch 1)])) (define/public (get-pos) blerch) )) Of course, our choice of x for the field name was arbitrary. We could have named it anything we wanted, so long as we gave it a proper interpretation.

But we could have done it differently ;; Constructor Template for Robot3% ;; (new Robot3% [y NonNegInt]) ;; y is the negative of the position of the robot ;; y is optional; default is 0 (define Robot3% (class* object% (StupidRobot<%>) (init-field [y 0]) ;; interp: the negative of the position of the robot. (super-new) (define/public (move-right) (new Robot3% [y (- y 1)])) ;; RETURNS: the x-position of the robot (define/public (get-pos) (- y)) )) Here the observable is not the value of any field. The observation method translates the field value into the external value of the observable.

Or we could have done it very differently ;; Constructor Template for Robot4%: ;; (new Robot4% [y ListOfRacketValue]) ;; x is any Racket list whose length is equal ;; to the position of the robot. ;; x is optional; default is 0 (define Robot4% (class* object% (StupidRobot<%>) (init-field [x empty]) ;; Interp: ;; a list whose length is equal to the position ;; of the robot (super-new) (define/public (move-right) (new Robot4% [x (cons 99 x)])) ;; RETURNS: the x-position of the robot (define/public (get-pos) (length x)) )) Puzzle: the other three implementations would work fine if we had a move-left method as well as move-right. How could you modify this implementation to handle move-left? How would you change the contracts and purpose statements of the other implementations to accommodate this change?

All three of these implementations have the SAME observable behavior no combination of scenarios and observations can tell them apart! If these are the only methods and observations we have on these objects, then we don't care which implementation we use– they will behave the same in any program. We could even write something like

Choose a random implementation Returns a random number between 0 and 3 ;; -> StupidRobot<%> (define (new-robot) (local ((define i (random 3))) (cond [(= i 0) (new Robot1%)] [(= i 1) (new Robot2%)] [(= i 2) (new Robot3%)] [(= i 3) (new Robot4%)))

Contracts and Interfaces (again) ;; move-right-by-distance ;; : StupidRobot NonNegInt -> StupidRobot (define (move-right-by-distance r n) (cond [(zero? n) r] [else (move-right-by-distance (send r move-right) (- n 1)])) This works with ANY object whose class implements StupidRobot<%>. Contracts should be in terms of interfaces, not classes.

We need to change the way we write tests Our tests should work with any implementation of StupidRobot<%> . We construct a scenario, in which we create some objects, send them some messages, and see what the observables are. The tests talk only through the interface.

A Simple Scenario (begin-for-test (local ((define r0 (new-robot)) ;; move r0 right twice (define r1 (send (send r0 move-right) move-right))) ;; get-pos should then return 2 (check-equal? (send r1 get-pos) 2))) (define r1 (move-right-by-distance r0 3))) 3)))

Observables in the problem sets In our problem sets, we've required you to provide just enough observables so that our automated testing routines can see if you've solved the problem. In a test, we create a scenario and then check the observables of the final state. The set of observables in the problem set is purposely minimal, in order to give you the maximum freedom in implementing the objects.

You may need to add some observables for debugging The set of observer methods in the problem sets is purposely minimal, in order to give you the maximum freedom in implementing the objects. You may need to add some observation methods for your own testing and debugging, so you can see what is going on inside your objects. That's ok, but give them names like for-test:whatever and do NOT use them for any other purpose.

Example of a scenario using a test method (define w1 (make-world 5)) (define w2 (send w1 on-key "n")) (define w3 (send w2 on-key "n")) ... (check-equal? (length (send w3 for-test:rectangles)) 2 "After 2 'n's, there should be two rectangles")

You can’t use equal? on objects In Racket, equal? on objects measures whether its arguments are the same object. In Racket, you can have two different objects with exactly the same values in the fields. We say that objects have identity. This will make more sense next week. In the meantime, here’s an example:

Example of object identity (let ((r1 (new-robot))) (equal? r1 r1))  true (let ((r2 r1)) (equal? r1 r2)))  true (let ((r1 (new Robot1%)) (r2 (new Robot1%))) (equal? r1 r2))  false

Luckily, most of the time we can avoid this. Usually, we’re not interested in whether we have the same object. We just care that our new object has the right observable properties.

Key Points of this lesson We can only test observable behavior observables are not fields We need to decide what properties are important, and test those. problem set will specify the interfaces that our tests rely on. we can’t use equal? test by creating scenarios and checking whether your objects have the right properties afterwards. ok to add for-test: methods, but only for debugging.

Next Steps Study example 09-4-testing.rkt in the Examples folder. If you have questions about this lesson, ask them on the Discussion Board Go on to the next lesson