Presentation on theme: "TDD Basics Tom Clune presiding Principles Tests should not duplicate implementation Tests should strive to be orthogonal Tests do not uniquely determine."— Presentation transcript:
TDD Basics Tom Clune presiding
Principles Tests should not duplicate implementation Tests should strive to be orthogonal Tests do not uniquely determine implementation Tests should be fine-grained (small increments). Specific tests should be derived from: –Basic requirements –Simplest cases –Limiting cases –Triangulation Only implement to just pass the last test. Always check that tests initially fail.
Tests vs Implementation Writing tests is a somewhat different art than writing an implementation. –Requires practice and creativity –Tests drive implementation, but … –Tests do not completely constrain implementation Examples of bad/useless tests: –Using method to be tested to specify the “expected” clause in an assertion: expected = f(3.) call assertEqual(expected, f(3.)) –Reimplementing method to be tested to specify “expected” clause in an assertion Sometimes a test is based upon an alternate implementation. –E.g. brute force implementation.
Example Geometric Sum a0 = 1. ratio = 0.5 s = 0 n = 2 do i = 0, n - 1 s = s + a0 * ratio ** i end do expectedSum = s call assertEqual(expectedSum, geometricSum(a0, ratio, n)) real function geometricSum(a, ratio, n) geometricSum = sum( (/ (a * ratio ** (i-1),i=1,n) /) ) end function geometricSum real function geometricSum(a, ratio, n) a_1 = a a_n = a * ratio ** (n-1) geometricSum = (a_n - a_1) / (1 - ratio) end function geometricSum
Orthogonality Developers should strive to create tests which are mutually orthogonal. –Strive for each test to cover just one implementation “feature”. –Tests which are already covered by other tests are wasteful. –Non-orthogonal tests may be limited in their ability to clearly isolate a single defect. I believe that exceptions to this rule are frequent, but the goal should be born in mind.
Determinism Many aspects of implementations are not driven directly by requirements. –Tests only verify behavior of implementations. (Tests are functional implementation of requirements.) –Tests aid the development of an implementation by focusing attention on smaller bits of algorithm. There is no magic. –Especially true for performance - consider multiple algorithms for Sorting (quick, bubble, …) Random number generation (LCG, …) Fourier transform (fast vs slow) Caveat: may need to “invent” tests to cover intermediate steps –One step/sweep of sorting algorithm –Butterfly operation for FFT –Row reduction for matrix solver
Test Granularity Each test should generally only drive a few lines of implementation. –Developers often overestimate their amount of code which can be written without introducing a defect. –Discipline requires practice. –The first test for a unit often drives creation of a large amount of “boiler-plate” code. This is ok. New module, derived type, constructor, and at least one method. We should try to create templates for most of these items. If implementation bogs down, backup and make test even smaller/simpler.
Minimal Implementation Avoid the temptation to implement beyond what is required by the test. –Any such extension is not itself being tested, so might have defects. –“You ain’t gonna need it!” - XP mantra –Save time for creating more/better tests. This is where creative energies should mostly be applied for TDD. Following this minimal approach requires practice and discipline.
Detailed Example Implement a “stack” (LIFO) of integers. –A stack is a structure which enables addition and retrieval of items where retrieval is always of the last item added. Methods: –new()/clean() ! Constructor and Destructor –push() ! Add n to stack –pop() ! Return n from stack –numElements() ! Return number of elements currently in stack.
Excercises Start from scratch and implement a “queue” (FIFO) analogous to the “stack” in the previous examples. –If you are strong with F90, stretch yourself by using pointers to implement a linked list instead of fixed maximum size. The differences are interesting. Bonus exercise: Starting from scratch use TDD to create a routine which returns the prime factors of a given integer as an array.