test-driven development (tdd) - bguasharf/spl/tdd.pdf · spl/2010 what is tdd? 3 changing code...
TRANSCRIPT
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
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
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
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 Javadocexpected behavior
24
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
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
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-condition
of 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
● 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