Unit Testing Database Applications
Unit Testing Database Applications
Outline - Overall
Testing in general Unit testing
The xUnit family of unit test frameworks
Database applications
A table wrapper example
About Myself
Education
Master in computer science (database systems) 1994 Ph.D. in computer science (database systems) 1998
Afraid to make changes to existing code Nice emails from the open source community
Livslang Lring 20. august 2004 4
Disclaimer
556 304
7
Test Purpose
Test of performance
TPC*C, TPC*H, TPC*R, and TPC*W (www.tpc.org) Home-grown benchmarks
N
Test of correctness
Formal verification Code reviews and walk-throughs Compile-and-run Stocastic testing (n-solutions) Unit testing
Livslang Lring 20. august 2004 8
Code
Livslang Lring 20. august 2004
[source http://www.stsc.hill.af.mil/stscdocs]
Integration Test
Testing of interfaces between subsystems How well do the subsystems fit together?
Unit test
Testing a single unit (or module) of code Uses stubs or drivers for interfaces to other units Is the foundation working?
Livslang Lring 20. august 2004 10
Extensions to JUnit
11
Motivation JUnit
It is nearly impossible to do a good job at software
error removal without tool support Used as model for many other unit test frameworks Instant binary feedback to developers Easy to use Widely used (even students use it, under pressure!) Open source (and free)
There are many extremely expensive commercial products related to software testing
design
imp.
test
stop project
start project
stop project
Done by programmers, for programmers! Not a formal validation and verification of a program!
14
PerlUnit (Perl)
http://perlunit.sourceforge.net/
PyUnit (Python)
http://sourceforge.net/projects/pyunit/
RubyUnit (Ruby)
http://www.ruby-lang.org/
Unit++ (C++)
http://unitpp.sourceforge.net/
Supplied by JUnit
Supplied by programmer
16
Test Suite
Test Case
Test Method
17
/** Tear down the test fixture */ protected void tearDown(){ // empty } }
Livslang Lring 20. august 2004 18
/** Second test method */ public void testEquals(){ assertTrue("HardDrives are not equal", seagate1.equals(seagate2)); <snip> } }
Livslang Lring 20. august 2004 19
[Source: www.junit.org]
20
time
time
Have more KLOC test code than real code Makes your low-level code much more reliable!
Solid foundation for integration and system tests
JUnit Extension
Test coverage
Clover (commercial) http://www.cenqua.com/clover/ NoUnit (open source) http://sourceforge.net/projects/nounit/
MockObjects
MockMaker http://mockmaker.sourceforge.net EasyMock http://www.easymock.org
Performance testing
JUnitPerf http://www.clarkware.com/software/JUnitPerf.html Daedalos http://www.daedalos.com/EN/djux
Database testing
DBUnit http://dbunit.sourceforge.net/
N
Overview of interfaces provided by table wrapper API The wrapper in an application stack
23
Table 1 Table 2
equal(<id> 1, <id> 2) return boolean update(<id> , values) return boolean delete(<id>) return boolean id exist(<id>) return boolean id
24
25
Views
Tables
Outline - DBMSUnit
Motivation
Problems existing frameworks
27
Environment
output
Persistency
Focus on main memory data structures Side effects (procedures not functions) Manual clean up between executing test cases necessary
Two-valued logic
In a database context three-valued (true, false, and unknown)
Livslang Lring 20. august 2004 29
SemID
Student k
Participant
Course h
CPR 1 Semester 1
SemID
StartDate
EndDate
30
31
Test Case
Framework
setup() test_1() test_n() teardown()
Test Case
32
Semester
Livslang Lring 20. august 2004 34
Two tables
public void setup(){ // semester table insert(row1) insert(row2) // course table insert(row3) insert(row4) }
DBMSUnit
TestCase setup() teardown() use() disuse() TestSemester semesterId = sm47 NotSemesterId = sm99 prerequests = null test1() test2() test3()
37
TestParticipant semesterId = TestStudent.semesterID studentID = TestStudent.studentID courseID = TestCourse.courseID prerequests = TestCourse, TestStudent test1()
38
Teardown method
Responsible for tearing down the text fixture
Use method
Responsible for make the public variables exist in the test database.
Disuse method
Responsible for make the public variables non-existing in the test database.
Semester
40
41
void teardown() // own teardown not part of any test method <snip> // tell that test case is longer in use stack.pop() // teardown what this test case depends on for unit_test in reverse(prerequests) unit_test.disuse()
Livslang Lring 20. august 2004 42
void disuse() // make public variables illegal testDeleteN() testDelete1() // teardown myself self.teardown()
Livslang Lring 20. august 2004 43
Insert, update primary key Select, update other than primary key (on specific data) Insert specific primary keys, update (on specific primary keys)
Slight modification
N
Trade-off
N
P St C Se Stack
Participant
use()
Course
use() use()
Semester
Student
use()
test1()
45
P St C Se Stack
Student
46
Requirements to Users
Must publish keys
Inserted in underlying table(s) Updated in underlying table(s) Optional keys guaranteed not to be in underlying table(s)
Must comply with rules for modification Specific order in which test methods are executed
Defaults to the textual order of the file
47
DBMSUnit Extension
Support multiple users running tests concurrently
Goal: Speed up execution of large test suites
48
Extensions
Semester
49
Student
Course
Semester
50
Extensions
Setup and teardown methods must be redefined Stack must be added Requirements must be followed
N
51
Oracle Specifics
Flashback queries
Multi-transaction rollback Ensures that flash back is to a consistent database state Removes the need for the teardown methods
N
Workspace management
CVS-like multiple versions of rows Saves changed rows to private copy of the database
N
Private to the user that changed the rows Public to all users of the database
Simple to use
Looks-and-feel of existing xUnit frameworks
53
Outline - Experiences
Overall experiences
The technical side The human side
Test patterns
Ideas for how to unit test database applications
54
Experiences
It is non-trivial to get the test framework to work! The first unit test takes a long time to get the up and running due
to dependencies Building unit tests takes quite a long time and is repetitious in nature (reads boring)
Auto-generation can make it more interesting
When unit test first build, repeated execution is very simple! Good error messages are essential for quickly finding the test
methods (and test case) that failed. Ripple effects could not be avoided due to coupling between test methods and test cases!
However, not that hard to find the source of the error
Experiences, cont.
It is important that the test cases can use the data that is
delivered with the database application.
Can be modelled with public variables (and no insert methods)
Invariants are good for detecting that an error occurred but not
which error
As an example, a simple count on the number of rows in a table
Encapsulate all calls to the system clock such that the time can
be frozen and the test always be done at a specific time.
Should also be applied to random value function Mock objects can be used for this
56
Experiences, cont.
It is important to separate test code from the real
code. Symmetric methods are a thrill to test (high testability)
e.g., object2String, string2Object
58
Test Patterns
Minimal/maximal pattern
Minimal case only the required attributes specified Maximum case all the attributes specified
Repeat pattern
Run the insert test methods twice, should raise exception Run the copy test methods twice, should raise exception
Commit pattern
Execute all test method in a single transaction Execute each test method in a separate transactions
Livslang Lring 20. august 2004 59
Future Work
Stepping through the execution of each test method Fully integration with the utPLSQL framework Making DBMSUnit a JUnit extension Performance measurements on proposed performance improvements
60
61
Reklameblok
Modelbaseret test
Vejen til mere effektiv test
Fr. Bajers Vej 7E Rum B2-109 Tid: Onsdag 25. august 13.00 16.00 http://www.ciss.dk
63
Conclusion
Unit testing
is an inexpensive test approach is very helpful in software maintainance phase. can be the difference between success and fiasko
JUnit Screenshot
[Source: www.junit.org]
66
67
Semester
SemID
1 2
Se Stack
Semester procedure run begin setup(); test_insert(); test_update(); test_exist(); teardown(); end;
Livslang Lring 20. august 2004
procedure test_insert setup test_update teardown test_exist begin update(SemID, SemID_update); if (exist(not_SemID)) then stack.push(); delete(SemID_update); insert(SemID); raise not_not_found_exception; public if delete(SemID); (not constant SemID := 1; then end if; exist(SemID_update) public commit; raise constant SemID_update:= 2; if (not update_not_found_exception; exist(SemID)) then end; end if; not_found_exception; public stack.pop(); constant notSemID := 3; raise end; end if; end;
68
Student
SID SemID
Semester
SemID
St Se Stack
Student
procedure run begin setup(true); test_insert(); teardown(); end;
procedure teardown setup procedure procedure test_insert begin begin begin semester.setup(false); delete(SID_update); insert(SID, semester.SemID); stack.push(); delete(SID); public constant SemID := 1; commit; if (not exist(SID, semester.SemID)) then public constant SemID_update:= 2; stack.pop(); raise not_found_exception; public constant notSemID := 3; semester.teardown(false); end if; end; end; end;
69