test-driven development (tdd)
Post on 09-Feb-2016
31 Views
Preview:
DESCRIPTION
TRANSCRIPT
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 exampleset_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
JUnitJunit: 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
top related