3 Adt&tdd

Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 33

Abstract Data Type

and Test-Driven
Development
Unit 3
CPRG304 – Object-Oriented
Programming III

Kitty Wong © 2023


The 4 characteristics of OOP
• OOP is all about data modelling
• Identify objects of the system and how they relate to each
other

1. Inheritance – “is a”
2. Polymorphism – “one name, many forms”
• Subtype or parametric
3. Encapsulation – “time capsule”
• Orthogonality (separation of concerns/responsibility)
4. Abstraction – “need-to-know basis”
Abstraction
• Suppressing details to emphasize on what is relevant at
the moment
• Represent data at a conceptual level without any details

• Provides high level views of objects and their


behaviours
• Enables us to forget about the low level details and
concentrate on the problem at hand
• Level of abstraction will depend upon the view point of the
user and the problem to be solved
Data
• Definition: independent facts, observations, or events.

• Example: Data about a student


• Name
• ID
• Program
• Year
• GPA
Data vs. Information
Low <------- level of abstraction ----- High
Data  (apply meaning)  Information

• E.g. Data: red octagon, white


border with letters S, T, O, P.

Information: A stop sign.


Data Type vs. Data Structures
• Data Type: classification of data, logical description that
determines possible values of the type and the
operations that can be done on it
• E.g. integers – whole numbers, no decimal points

• Data Structure: implementation of data types, concrete


actual representation of data, a particular way of
storing and organizing data
• E.g. Java INT – exactly 4 bytes to represent value from
-2,147,483,648 to +2,147,483,647 (=231, 1 bit for sign)
Abstract Data Types (ADT)
• A (mathematically) specified model of data-storing
entities with operations on that data
• An ADT specifies what each operation does, but not how it
does it!
• A data type that does not specify an implementation
• Logical description of the data and operations/rules to access or
manipulate the data
• An ADT is realized/implemented by a concrete data structure
• E.g. integers, can add, subtract, compare, etc.
• E.g. text, can append, concat, get size, etc.
Abstract Data Types (ADTs)
• Implementation independent!
• E.g. SHORT or INT for integers
• E.g. String class for text, can be done with arrays or linked
lists
• Can be implemented in any language

• ADTs defines the WHAT, not the HOW


• Abstraction separates the purpose from implementation
• Provides encapsulation, orthogonality and flexibility to
maximize effectiveness and efficiency
Why ADTs?
• Implementation can change but the ADT doesn’t!
• E.g. You design various uses for crushed ice
• You don’t need to know how the ice dispenser works, you don’t care
and is not distracted by the engineering details = abstraction
• Eventually, someone must build the dispenser, many techniques are
possible (implementation)
• Mechanisms of certain operations are encapsulated with the internal
structure elements = encapsulation
• If the dispenser is surrounded by walls, the machine’s interior mechanisms
are not only hidden from the user but also inaccessible = information
hiding
• If dispenser was built with modular design, you can improve crushing
of the ice without affecting the other modules = modularity
ADT vs. Data Structure
• ADT is a collection of data and a set of operations on
that data
• This is the CONTRACT – what the ADT promises to others,
what the ADT is and how you can interact with it
Java ADTs
• Usually done as an
Interface, mostly to
represent large sets
of data (collections)

• A class can extend


multiple interfaces =
only way to achieve
multiple inheritance in
Java
Designing an ADT
1. What data does a problem require?
2. What operations does a problem require?

• An ADT specification should NOT include


implementation details
• We can fill out the ice dispenser with tap water, distilled water,
juice or wine – the operations will not change
• You can use an ADT without knowledge of its
implementation
Designing an ADT – Methodology
1. Understand the data type abstractly
2. Write a specification of the data type
3. Use the data type – write a test program first!
• Test-Driven Development (TDD)
4. Select appropriate data structures and implement the
ADT
5. Analyze implementation, compare with Java
Collection API and other implementations
Designing an ADT in Java
• We need to specify how users can interact with the data
• What inputs are needed
• What outputs are generated
• What can go wrong?

Parameters Method Return

Exceptions
Common Operations (Method Types)
• Constructors – creates the ADT and initializes state
• Accessors (Getters) – returns a value from the
collections or the whole collection, or some information
about the ADT
• next(), min(), size()
• Mutators (Setters) – modifies the collection in any way
• add(), replace(), clear()
• Transformers – similar to getters but changes the data
value or type being returned
• isEmpty(), calcVolume(), calcBaseArea()
CounterADT Exercise
• We need a class called Counter. An object of this class
will be used to count things, so it will record a count
that is a non-negative whole number. This class will
need to set the counters to a given integer, increase the
count by 1, and to decrease the count by 1. We will
need to be able to return the current count as an
integer and a string to be displayed on the screen. It is
important to be able to determine if the counter is zero
at any given point in time.
Step 1 - Understand
Counter
- counter: int
+ setCounter( num: int ) : void
+ incCounter() : void
+ decCounter() : void
+ getCounter() : int
+ toString() : String
+ isZero() : Boolean

• What can go wrong? Non-negative value!


• Throws InvalidCounterException when object is mutated
Step 2 – Write the Interface
• CounterADT.java

• Documentation is CRITICAL!
• Just as you would read the Java API to understand how to use
a class and its methods, we need to be VERY precise in our
JavaDoc to let users know how to use and what to expect
from our ADT!

• Preconditions – what must be in place for this


operation to execute as promised
• Postconditions – what the ADT promised to do
Step 3 – Use the ADT
• Does the ADT solve the problem you want it to solve?
• “Writing the right code”

• Does it solve the problem in a good/efficient way?


• “Writing the code right”

• We need to make sure our ADT will do what it promises


to do
• The only way to ensure this is to “test” it!
Testing
• “Writing the right code”
• Determines the correctness of our software
• Detects defects = bugs!

• Definition: evaluating a property of interest in S/W to


determine if it:
1. Meets the design requirements
2. Responds correctly to all inputs
3. Performs its function
4. Achieves the desired results
Test-Driven Development (TDD)
• Design tests as we’re designing our classes
• Turn software requirements into specific test cases

• This allows us to concentrate of what is important – the


requirements to solve the problem
• Proven to produce simpler designs and cleaner code
• Guarantees that all functionalities are tested – 100% coverage
(every single line of code is tested)
• Always available when code is changed (regression testing)
TDD in a Project
• The earlier we find a bug, the cheaper it is to fix it!
Time detected
Cost to fix a defect
Requirements Architecture Construction System test Post-release
Requirements 1× 3× 5–10× 10× 10–100×
Time
Architecture – 1× 10× 15× 25–100×
introduced
Construction – – 1× 10× 10–25×

• Some components of functionality that cannot be easily


tested except for the developer!
• Especially in the back-end where there may not be a GUI or
entry point that is independent from the rest of the system
TDD Lifecycle
TDD in Action
• Software Design is top-down, software testing is
bottom-up!

• TDD is aimed at component/unit level


• Integration, system and acceptance tests must still be
performed later

• In Java – we’re testing EACH and EVERY method!


Input values
• We can’t possibly test EVERY single input value!
• E.g. int add( int x, int y );
• 1+1=2
• 1+2=3
• ………
• 1 + 100 = 101
• 1 + 101 = 102
• Etc.

• Choose a subset of inputs that will represent all


possible case!
Combinatorial Test Design
1. Expected values
• The “happy path”: what a reasonable input would be
• Name – all characters
• Age – integer value > 0
• Months – integer value between 1 and 12
2. Boundary values
• Unreasonable values
• Months – integer 0 and 13?
• Throws exceptions?
3. Strange values
• Unexpected values
• Age – negative numbers? “abc”? Null?
Preconditions
• This is why we NEED these in our documentation!

• Our ADT is what happens when an input is used as an


argument to our method and what we promise to do
with it!
• If you write it clearly in the JavaDoc – this is the contract!
• E.g. Expecting only non-negative integer values
• If user puts it anything else, your method doesn’t work… and it’s NOT
suppose to!
• But if your method can and will take ANY value – test them
ALL*!
CounterADT Tests
• Use Eclipse to generate the JUnit test stubs!
• Create Counter class that implements the CounterADT interface
• Create new source folder called “test”
• Create new JUnit Test Case based on Counter
• For this course, please use JUnit 4
• Placed in folder “test”, not “src”!
• Name the package to “counterUnitTests” or similar
• Choose “Generate Comments” to give yourself a good starting point for
proper documentation
• Choose Class under test to be the new Counter class you created
• Select all Counter methods and toString() from Object
• Check that there are no compilation or runtime errors
Test Structure
1. Setup
• Put the Unit Under Test (UUT) or the overall test system in the state
needed to run the test
2. Execution
• Trigger/drive the UUT to perform the target behavior and capture all
output, such as return values and output parameters. This step is
usually very simple
3. Validation
• Ensure the results of the test are correct. These results may include
explicit outputs captured during execution or state changes in the UUT
4. Cleanup
• Restore the UUT or the overall test system to the pre-test state. This
restoration permits another test to execute immediately after this one
JUnit
• setUp()/setUpBeforeClass(): create some objects to test with
• tearDown()/tearDownAfterClass(): remove/reset test objects
• Assertions: evaluate actual state of test objects against expected state
• assertEquals()
• assertNotEquals()
• assertTrue()
• assertFalse()
• assertNull()
• assertNotNull()
• assertSame()
• assertNotSame()
• assertArrayEquals()
• assertThat()
• fail()
TDD Methodology
1. Write a unit test with various inputs
• Start with getters, then setters, others
2. Check that the test fails
• It’s ok! Nothing is “wrong” – this is normal for TDD!
3. Write just enough code to pass the test
• Don’t try to add anything extra or fancy – we just want the
code to do what it needs to do, nothing more, nothing less!
4. Check all tests – all should pass, if not – debug
• Check that the new code didn’t break something!
5. Repeat until all units are complete
Effective Unit Tests
• Runs fast – they have short setups, run times, and
break downs
• Run in isolation – you should be able to reorder them
• Use data that makes them easy to read and to
understand
• Use real data (e.g. copies of production data) when
they need to
• Represent one step towards your overall goal
Exercise
• DateADT – practice the TDD methodology from design
of the ADT to writing complete tests to “write the right
code”!

You might also like