Date post: | 30-Mar-2015 |
Category: |
Documents |
Upload: | arlene-knotts |
View: | 227 times |
Download: | 3 times |
SPL/2010SPL/2010
Test-Driven Development (TDD)
1
SPL/2010SPL/2010 2
SPL/2010SPL/2010
What is TDD?
3
changing code without modifying external functional behavior
SPL/2010SPL/2010
What is programming?
● writing code: addressing requirements/solve a problem● verify: code answers requirements / program
performs according to specifications● requirement/specification/verification:
● complex problem is broken in small problems– can be solved by writing short pieces of code
● specify (define) and verify small pieces of code
4
SPL/2010SPL/2010
What is Development?
●as we develop more, we understand better…
● developer asks better questions, and gets better answers
● requirements of program change over time ● existing code must be changed...
● difficult to predict how other parts of the code are affected
5
SPL/2010SPL/2010
What is Testing?
● correctness – code performs by specification● 3 types of tests:
● Unit tests: a particular module is working properly:– Implemented by the programmer. – Simple test cases for all functions and methods. A test case is
a program…● Integration tests: a combination of modules work well
together and exchange messages according to protocols ● Acceptance tests: a whole system under test to check
that the expected functionality meets the requirements– functional and performance criteria.
6
SPL/2010SPL/2010
Unit Test Terminology
● Test case: tests a single scenario of usage. ● one aspect of the protocol of the object: its
invariant, a single pre-condition / post-condition.
● Test suite: collection of test cases that fully verify the public protocol published by an object
● Object under test (OUT): the object being tested by a test suite
● test suite should focus on testing a single object - assuming all other objects perform correctly
7
SPL/2010SPL/2010
Unit Test Terminology
● Test coverage: the part of the code of the object that is executed by running a test suite
● every code line of OUT should be executed when running test suite● Test fixture: other objects that will interact with the OUT
● A test case must be self-contained: create, initialize and set all the required test fixtures before the test scenario can be executed. Test fixtures must be cleaned up after the test case is run.
● Mockup: a basic, test-specific, implementation for classes on which OUT depends.
● OUT is independent of other objects - avoid using existing objects ● mockup and the "real" object will usually satisfy the same
interface● Usually: mockup for data-retrieval, not for logical behavior
8
SPL/2010SPL/2010
Unit Test Terminology
● Positive test case: verifies that a public operation of the OUT performs as expected.
● Negative test case: verifies that a public operation of the OUT fails as expected
– a call to a method when a pre-condition does not hold properly throws an exception
● Repeatable and deterministic tests: runs the same test twice in a row - same result. ● test case cannot depend on external data or on
the timing of RTE scheduler
9
SPL/2010SPL/2010
Pseudocode example
set_hour (a_hour: INTEGER)
-- Set `hour' to `a_hour'
require
valid_argument: 0 <= a_hour and a_hour <= 23
do
hour := a_hour
ensure
hour_set: hour = a_hour
end
10
precondition
postcondition
SPL/2010SPL/2010
TDD = TFD+refactor
11
SPL/2010SPL/2010
What is TDD?
● Test first design - repeatedly first writing a test case and then implementing the code necessary to pass the test.
● It is the responsibility of the programmer to define the unit tests as part of the code delivery● requirements granularity level of the module ● module depends on other modules
12
SPL/2010SPL/2010
What is TDD?
● requirements on the code by writing a test● test case IS the expression of the
requirement● implement code to stand by the
requirements● code pass tests
13
SPL/2010SPL/2010
Test First Design cycle
● Define objects: responsible for specific functionality and interaction● Write tests for each OUT:
● Define the interface of the OUT● Define contract for each method of the OUT interface● Specify pre-conditions, post-conditions for each method
and invariant for the OUT. ● Write test cases for each invariant, pre and post-condition of each method in
the interface.● Write the code so it will pass the test● Run tests● Refactor! improve code design by removing code that "looks bad“
("code smells“)● Repeat process: code changes, rules change, test change● When needed - Break the contract, Redefine tests, Refactor code
14
SPL/2010SPL/2010
Design By Contract (DBC)
Concept that helps programmers express the requirements on an object, correctness criteria:● preconditions : things that must be true
before we invoke a method● postconditions : things that must be true
after a method is invoked● invariants: things that must be true both
before and after a method is invoked
15
SPL/2010SPL/2010
TDD benefits
● validation of correctness: errors caused by code/design modifications are caught quickly by programmer, immediately after the code is changed.
● courage to make code modifications and refactoring, a change will "break" the program.
● integration tests - test cases can help design intelligent integration tests.
● design improvement- writing tests before the OUT is implemented ensures OUT is indeed usable, that it is easy to prepare the context in which the OUT can be invoked.
● E.g.: OUT depends on too many other objects or global objects, - writing tests becomes very difficult - test cases reveal this - strong incentive to improve the design of the OUT
● documentation - test classes provide examples of code usage
16
SPL/2010SPL/2010
JUnit
Junit: a simple framework to write tests (in Eclipse) ● public default ctor● setUp() - prepare pre-conditions (@Before)● tearDown() - "clean up" the test object after a test has run
(@After), "undoes" what the @Before method did.● test method for each test case (@Test)
● JUnit run:● instance of the test class is constructed.● run setUp()● run test case● display test results (pass/fail)● run tearDown()
17
SPL/2010SPL/2010
Example: Stack data object
18
SPL/2010SPL/2010
TDD
● Requirement: write a simple Stack data object, what is a "Stack"?
● informal description:● Stack is a container of Objects. ● Objects are ordered in Last-In-First-Out order. ● Add/Remove an Object to Stack
19
SPL/2010SPL/2010
Interface
Turn Description into interface (formalize )
20
Javadoc: inline tag {@link URL}
SPL/2010SPL/2010
Generics
● it is a "code smell", to let a container receive any object (too general)… ● Object = haven't thought enough about
specific objects
21
SPL/2010SPL/2010
fill interface – what means to use a Stack?
● DBC: methods, preconditions, postconditions, invariants
22
SPL/2010SPL/2010
test class● test-class skeleton for the interface
23
SPL/2010SPL/2010
define tests
● think of complicated scenarios/ usage: parameters, exceptions, return values
● think of complicated behavior/sequences: – push-pop-pop, pop-Exception – isEmpty returns true or false, – push objects of different types <T>
● push a null - What should we do?● Change Stack API, - add Exception to push()● do not change interface - specify in Javadoc
expected behavior24
SPL/2010SPL/2010
Implement tests● TFD: think of tests BEFORE implementing● define Stack<Integer>● assume "this.stack" is already
instantiated● add @Before method to create the stack
25
SPL/2010SPL/2010
first tests
26
SPL/2010SPL/2010 27
SPL/2010SPL/2010
more complex tests
28
SPL/2010SPL/2010 29
SPL/2010SPL/2010
implement Stack interface
● write minimum! code to pass tests● if no test fails, we don't need to write code● passing code is valid Stack implementation.
● additional classes require their test cases● Tip: override (and test) toString method,
for all classes
30
SPL/2010SPL/2010
Refactoring tests
● test (positive) for push() ● pop() to test push() – circular● pop() changes the state of the stack
(removes top item)
31
SPL/2010SPL/2010
Refactoring tests
● Weak pop() test: - push an item and pop without exception● Better test:
● returns last element pushed on the stack;● stack has one element less● … a copy of testPush() - one test for two functions
● …complex post-conditions - rethink! - analyze contract of pop() ● pop() does 2 things:
● removes an item from the stack ● returns the value of this item.
● design (if possible) methods to do one thing - reusable, testable.
32
SPL/2010SPL/2010
Refactoring tests:6 principles for writing testable interfaces
1. Separate commands and queries:● Queries return a value and do not change
the visible state of the object– methods with side-effect on the object - push()
● Commands change the internal state of the object and do not return values– functions that only get information - isEmpty()
33
SPL/2010SPL/2010
Refactor pop()
● pop() does not stand by this rule: it is neither a command nor a query
● replace pop() with primitive methods:● top() returns the value of the top object● remove() removes top object from the stack
● keep pop():● T pop() { T top = top(); remove(); return top; }
34
SPL/2010SPL/2010 35
SPL/2010SPL/2010
2. Separate Basic Queries from Derived Queries
● Is this test strong enough? What could go wrong?
● Change hats: assume person writing the code tries to pass the test with minimal effort● push=replace● testStackLIFO – 3 item stack
36
SPL/2010SPL/2010
● count() – add new query that indicates how many elements are stored in Stack
●
use to write post-conditionof push(), remove()
● @pre(count()) - refer to value of count() before command is executed
● isEmpty(): count()==0● count() - primitive query
● isEmpty() - derived query
● post-condition computed based on primitive query
37
SPL/2010SPL/2010
2. Separate basic and derived queries● Derived queries can be specified in terms of
basic queries.
3. Define derived queries in terms of basic queries● Define the post-conditions of derived queries
in terms of basic queries only
38
SPL/2010SPL/2010
4. For each basic command, write post-conditions that specify values of basic queries
● a command modifies the state of OUT● test modification, with basic queries:
● review post-conditions of command by available basic queries of object
● if no basic queries is affected – extend/revise interface
● isEmpty() is a derived query (count()) – can be removed
39
SPL/2010SPL/2010
Refactor remove()● contract encoded in the pre, post-
conditions ● remove() removes one element from stack● cannot call when the stack is empty. ● WHICH element is removed (top)?● more specific contract● basic observe query
40
SPL/2010SPL/2010 41
SPL/2010SPL/2010
● Did this make our post-condition for remove() stronger?
● count() has been decreased by one● Do we need to check that the last element is
the one that is removed?● itemAt for all values of i=1 to new count() are
not affected● Did itemAt() break encapsulation?
● stack limit access to only the top element
42
SPL/2010SPL/2010
5. For each basic command and query, express pre-conditions in terms of basic queries
● reminder: basic queries are sufficient to capture post-conditions of basic commands
43
SPL/2010SPL/2010
6. Specify class invariants that impose “always true” constraints on basic queries
● class invariants: properties that remain true in all legal states: @inv count() >= 0
● verify contract of commands that affect count() cannot break this invariant. ● remove() : verify that the pre-condition
prevents count() from changing from 0 to -1
44
SPL/2010SPL/2010
Summary
● specify the interface of objects before implement ● interface specification includes contract for methods, expressed in
terms of @pre, @post and @inv conditions.● write tests for interface to verify contract is enforced before
implementation● design objects interface to be testable:
● Separate commands and queries.● Separate basic queries and derived queries.● Define derived queries in terms of basic queries.● For each basic command, write post-conditions that specify values of basic
queries● For each basic command and query, express the pre-conditions in terms of
basic queries.● Specify class invariants that impose “always true” constraints on basic
queries
45