2. Outline
Testability: what, why, how and when?
The Design Dilemma
Testability Features
Techniques for Developing Quality-
Oriented Software
Design for Testability
3. Testability .. What?
Testability = controllability & visibility
Controllability: the ability to apply inputs to s/w
under test & place it in specified states
Visibility: the ability to observe states and outputs
[what we see is what we test]
Design for Testability
4. Testability .. Why?
Improve software quality
Ease the test process and more support
for test automation
Cost reduction
The average software company spends 60-80% of
its development costs on supporting [Lidor Wyssocky]
Design for Testability
5. Testability .. How?
Investing more time in design
[A good design is a testable design]
Adding testability features
Using special techniques throughout
the development process
Design for Testability
6. Testability .. When?
From Day 1
Testers has to be engaged early in the
product development cycle
The product has to be open for design
changes to meet the testing requirements
Design for Testability
7. The Design Dilemma
The time given for design.... ?!
Using the perfect tools....
But in the wrong way ... !!!
?
modularity?
Polymorphism
scalability?
Encapsulation reliability?
reusability?
Inheritance
maintainability?
Data Abstraction
Design for Testability
8. Design Weakness: How &
Why
Symptoms of a bad design
Rigidity
Fragility
Immobility
Viscosity
Causes of a bad design
Lack of Abstraction
Violation of Encapsulation
Interdependence {between Objects & Layers}
Design for Testability
9. Abstraction & Interfaces
Separate interface from its implementation
use overriding to support polymorphism
When deriving a class,
inherit interface
not implementation
Implementation is either Extended
Or, Overridden Polymorphism
Design for Testability
10. Encapsulation
Data are Private, Methods are Public…,
& Implementation Details are Hidden
Encapsulating the data and the functions
operating on this data.
myThing[] things = thingManager.getThingList();
for (int i = 0; i < things.length; i++) {
myThing thing = things[i];
if (thing.getName().equals(thingName)) {
return thingManager.delete(thing); } }
return thingManager.deleteThingNamed(thingName);
Design for Testability
11. Interdependence
The Acyclic Dependencies Principle
(ADP)
Dependencies must not form cycles
3 ways of communication
Invoke Method Superiority Relation
Raise Trigger Inferiority Relation
Message Passing Peer Relation
Design for Testability
12. Class Design Principles
The Single Responsibility Principle
(SRP) class has only one responsibility
Each
The Open-Closed Principle extension
A module should be open for (OCP)
but closed for modification
The Liskov Substitution Principle (LSP)
Subclasses should be substitutable for
their base classes
Design for Testability
13. Copy
Reader Writer
Keyboard Printer Disk
Reader Writer Writer
Read Write Write
Keyboard Printer Disk
OCP Example
Printer
Write
Design for Testability
Write
Copy
Disk
Keyboard
Read
14. LSP Example
class Bird { class Penguin : public Bird {
public: virtual void fly(); public: void fly() {
}; error (“Penguins don’t fly!”); }
};
class Parrot : public Bird {
public: virtual void mimic();
};
void PlayWithBird (Bird& abird) {
abird.fly(); // OK if Parrot.
// if bird happens to be Penguin...OOOPS!!
}
Does not model: “Penguins can’t fly”
It models “Penguins may fly, but if they try it is error”
Run-time error if attempt to fly → not desirable
Design for Testability
15. Testability Features
Logging events & verbose mode
support
Assertions {make assumptions explicit}
Test points (fault injection hooks)
Scriptable installation process
Design for Testability
16. Testability Features (cont.)
Benefits of Adding Testability Features
Detecting internal errors before they propagate
Knowing the source and place of the problem easily
Ability to reproduce bugs many times
Notes
Time stamps may be useful in logging
Well describe and identifying the logged event
Agree on a standard format of logging all over the system
[The use of a stand-alone Logger]
Use variable levels of logging
Throwing exceptions upon an assertions violation may be
useful rather than just logging it as a warning
Design for Testability
17. Development Techniques
Defensive Programming
Assertions everywhere
Design by Contract
Test-Driven Development
Mock Objects
A generic unit testing framework that
supports TDD better than test stubs
Design for Testability
18. Design by Contract
Preconditions
what must be true when a method is invoked
Postconditions
what must be true after a method completes
successfully.
Class invariants
what must be true about each instance of a class.
Design for Testability
19. Test-Driven Development
Write tests first, then write the minimal code
the makes this test pass
Tests provide the required specification for
the developer {A part of the documentation}
Provides rapid feedback for the developer
Emphasizes fast, incremental development
Functionality is added in very small chunks
Ensures 100% thoroughly unit tested code
Design for Testability
20. Summary
Design Better
Use interfaces
Don’t violate encapsulation
Avoid interdependence and cycles
Class single responsibility
Class open for extension closed for modification
Class substitution (parent is more general than its child)
Add Testability Features
Logging
Assertions
Fault injection hooks
Enhance the Development Process
Design by contract
Test-driven development
Design for Testability
21. References
1. Bret Pettichord, “Design for Testability”
2. Jeffery E. Payne, Roger T. Alexander, Charles D. Hutchinson,
“Design-for-Testability for Object-Oriented Software”
3. Robert Martin, “OO Design Quality Metrics An Analysis of
Dependencies”
4. Rahul Tyagi, “Two principles to help create robust, reusable object-
oriented design apps”
5. Robert C. Martin, “Design Principles and Design Patterns”
6. Matt Weisfeld, "Object-Oriented Thought Process", Second Edition,
Sams Publishing
7. Wikipedia
8. http://www.johndeacon.net/OOAandD/top25GoldenRules.asp
9. http://www.artima.com/weblogs/viewpost.jsp?thread=132358
10. http://labs.cs.utt.ro/labs/ip2/html
11. http://www.mertner.com/confluence/display/MbUnit/Design+Standards
Design for Testability
Know your tool first An OOP language gives you the tools (like overloading, overriding, virtual functions, static functions, friend functions . . . Etc.)… to support the 4 properties of an object-oriented design which are Data Abstraction Encapsulation Inheritance Polymorphism but it is your responsibility to use them the right way to achieve the design goals of modularity, reusability, maintainability, scalability, (expandability, reliability) ..etc
Rigidity صلادة (opposite of coherence تماسك ) A design is rigid if it cannot be easily changed. Such rigidity is due to the fact that a single change to heavily interdependent software begins a cascade of changes in dependent modules. Fragility هشاشة (opposite of design reliability) the tendency of a program to break in many places when a single change is made. Simple changes to one part of the application lead to failures in other parts that appear to be completely unrelated. Fixing those problems leads to even more problems, and the maintenance process begins to resemble a dog chasing its tail. Immobility (opposite of reusability) the inability to reuse software from other projects or from parts of the same project. Viscosity لزوجة is when it is much easier to hack than to preserve original design (When faced with a change, engineers usually find more than one way to make the change.) Viscosity is resistance to fluid motion
Tips: Superclasses should be abstract . Don't instantiate superclasses even if you could. Only instantiate leaf-node, concrete classes. Interfaces are more important than implementation Public method (member function) signatures need more thought, care and attention than they are usually given. Inherit interface first, and implementation later Focus on type sharing (inheritance of interface) first. **Only use inheritance when primarily sharing type relationships and secondarily sharing implementation. If you want implementation alone, composition is a much better contender. Polymorphism means that similar objects can respond to the same message in different manners. Implementation is Extended for specialization only not adding new responsibility [Inheritance is Used for Specialization]
violation of the single responsibility principle (the class takes over other class responsibility !) This code is tightly coupled to the implementation of myThing in that it gets the name property, knows that it's a string, etc. This is a classic approach from the old days or procedural programming. It is NOT OO. Why you operate on data that is not yours? All it should know is how to ask a thing manager to delete a thing given its name ! This reduces Cohesion of the code !
OCP: - Be open for extension: module's behavior can be extended - Be closed for modification: source code for the module must not be changes Modules should be written so they can be extended without requiring them to be modified - A class must not depend on a concrete class! It must depend on an abstract class LSP = Inheritance should ensure that any property proved about supertype objects also holds for subtype objects
Consider a simple program which is charged with the task of copying characters typed on a keyboard to a printer. void Copy(){ int c; while ((c = ReadKeyboard()) != EOF) WritePrinter(c); } consider a new program that copies keyboard characters to a disk file: while((c = ReadKeyboard())!= EOF) if(dev == printer) WritePrinter(c); else WriteDisk(c); You modified “Copy” to extend it (??? !!!) it has to be closed for modification !!
Solution of this is to make 2 subtypes of birds (flying_birds and non-flying_birds )
Back to the testability definition of {visibility and control} and let’s see how can we achieve this by adding some features to the developed code to support both of visibility and control Assertions increase observability Example of a test point: consider the implementation of write_disk class.., you have to make the file_descriptor variable a member data variable in the class not a local variable; so that the tester can change its value to simulate a disk_full case and see how the system will handle it without making the disk full in reality. Test points are very useful for testing environment variables like temperature, I/O, network connections…etc. and other test cases that need simulating cuz making them happen in reality is very hard.
The time of the day class example -invariant of the class: its members 0<=(sec, min)<=60 and 0<=(hours)<=23 -precondition of set_hour(h) method: 0<h<23 -postcondition of set_hour(h) method: hour = h, min,sec unchanged
Test-driven development is a method of software development where tests specify interfaces of implementation and all code must have passed the tests.