Title: Unit Testing Tips and Tricks: Database Interaction
1Unit Testing Tips and Tricks Database
Interaction
2Overview
- Preaching about TDD
- What is a Unit Test
- Common Unit Testing Patterns
- Unit Testing Database Interactions
- Acceptance Tests With Databases
3Are you test infected?
- There are two kinds of people
- People who dont like it
- Cant be done attitude
- People who do like it
- I think I can attitude
4Test Driven Development Is Hard
- Conceptually, its simple!
- (Sure, just like OO is simple)
- Its a learning process. It will be hard at
first. - Be creative!
5Im Guilty
- I'm guilty. I am a hack. I am an "impurist". I
often don't do "pure" TDD. - I find TDD easier to do for some tasks than
others. I do whatever I feel like. However, I
like what I get with TDD, so I lean towards it - Unit tests are good even if you dont do TDD!
6There Are Many Kinds Of Tests
- Acceptance tests, user test, integration tests,
unit tests black box, white box - All tests have merit if they can detect bugs.
- Tests only have value if they are run!
7Unit Tests
- From developer's point of view.
- Tests the smallest amount of a system that is
interesting. - Often just one part of one class!
- Highly automated
8Unit Test Rule Of Thumb
- If you are having trouble writing a unit test
- or (for those of you who aren't test infected)if
it's "impossible" to write a test for your
system, - You are trying to test to much. Test a smaller
chunk.
9But How?
- Sometimes objects have complex behaviors,
extensive state, and tight relationships. This
makes tests difficultset up is difficult and
time consuming, and objects cannot be isolated. - (But wait, thats not right! Right?)
10Loosening The Coupling
- Introduce interfaces between complex objects.
- Create a mock object to stand in for the complex
object. - Repeat as needed. (Be creative.)
11Creating Interfaces
- If it's our object, just create an interface!
- if it's not our object,
- create a mock that extends the object and
overrides all its methods (works sometimes) - create an interface anyway and create an adapter
for the foreign object - Example WallClock
12Example WallClock
Interface
public interface WallClock long getTime()
Wrapper for normal system service
public class DefaultWallClock implements
WallClock public static final WallClock
INSTANCEnew DefaultWallClock() public long
getTime() return System.currentTimeMilli
s()
13Mock Objects
- Start out as simple as possible (throw exceptions
on all methods). - Add recording of incoming method calls
- - ex RecordingMockObject, Thumbprinters
14Example ReportingMockObject
public class ReportingMockObject
StringBuffer m_stringBuffernew StringBuffer()
//---------------------------------------------
------------------- public String
getActivityRecordAndReset() String
sActivityRecordm_stringBuffer.toString()
m_stringBuffernew StringBuffer()
return sActivityRecord
//------------------------------------------------
---------------- public void
recordActivity(String sMessage)
m_stringBuffer.append(sMessage)
15Example MockClientSession
public class MockClientSession extends
ReportingMockObject implements ClientSession
public MockClientSession() public
void flushOutgoingBuffer()
recordActivity("fOB") public void
setInterval(int nUpdateIntervalMilliseconds)
recordActivity("sI("nUpdateIntervalMilliseco
nds")") public void
notifyNewOutgoingData()
recordActivity("nNOD") public String
getClientName() recordActivity("gCN")
return "mockClient" //
16Example MockMultiTableSessionListener
public class MockMultiTableSessionListener
extends ReportingMockObject implements
MultiTableSession.Listener public
interface Thumbprinter String
getThumbprint(MultiTableSession.Update update)
String getThumbprint(SessionState
sessionState) //
private final Thumbprinter m_thumbprinter
// public MockMultiTableSessionListener(Thumb
printer thumbprinter)
m_thumbprinterthumbprinter //
public void sessionStateNotification(SessionState
sessionState) if (truem_bLogSessionSta
teNotification) recordActivity("sSN(
"m_thumbprinter.getThumbprint(sessionState)")")
17Mock Objects, contd
- Add facility for sending back canned responses
- (ex, setNextReply, setFailOnNextRequest)
18Example WallClock
public class MockWallClock implements WallClock
private long m_nextTimes private
int m_nNextTimeIndex public void
setNextTime(long nNextTime)
setNextTimeList(new long nNextTime)
public void setNextTimeList(long nextTimes)
Require.neqNull(nextTimes,
"nextTimes") m_nextTimesnextTimes
m_nNextTimeIndex0 public long
getTime() Assert.neqNull(m_nextTimes,
"m_nextTimes") long nNextTimem_nextTimes
m_nNextTimeIndex m_nNextTimeIndex
if (m_nextTimes.lengthm_nNextTimeIndex)
m_nextTimesnull
return nNextTime
19Mock Objects, contd
- Do whatever you need
- Often one mock object will support all tests for
a given object, but can create special ones for
certain tests - Often, one mock object will support tests for
many objects that interact with it
20Mock Object Frameworks
- EasyMock (http//easymock.org)
- jMock (http//www.jmock.org/)
- YMMV!
21Object Mother (?)
- Sometimes you will need a complex data structure
set up. Refactor mercilessly. - Especially if you need canned data that is
ancillary to the test, it is often worth while to
factor creation out into a static method in a
util class so you can use it as necessary
thereafter.
22Testing Accessor
- Problem there are private methods you would like
to test, or private members you would like to
inspect for your test - You could make them public, but they really are
private - Alternative an inner class! TestingAccessor
23Example TestingAccessor
//
// testing private
WallClock m_wallClockDefaultWallClock.instance
private IStepper m_getConStepperDefaultStepper
.instance private IStepper
m_maintStepperDefaultStepper.instance
public class TestingAccessor public
void setWallClock(WallClock wallClock)
m_wallClockwallClock public void
setGetConStepper(IStepper stepper)
m_getConStepperstepper public void
setMaintStepper(IStepper stepper)
m_maintStepperstepper public void
setNextOverdueConnectionCheck(long
tsNextOverdueConnectionCheck)
m_tsNextOverdueConnectionChecktsNextOverdueConne
ctionCheck public int
getAllConnectionsSize() return
m_allConnections.size() public int
getUnusedConnectionsSize() return
m_unusedConnections.size() public int
getTotalConnections() return m_nTotalConnections
public void cacheMaintenaceThread()
DBConnectionPool.this.cacheMaintenaceThread()
public void doNotifyAll() synchronized
(m_oStateLock) m_oStateLock.notifyAll()
public TestingAccessor getTestingAccessor()
return new TestingAccessor()
24Testing Database Interactions
- You should be thankful! All the database classes
are interfaces already! - Create mocks and away you go
- Insert / update / delete easy
25Testing Database Interactions, Contd
- Read trickier
- Can use hard coded expectations
- Mocks will act as factories statements return
record sets - load your mock statement with the mock
record set to return. - load your mock connection with the mock
statement to return. - Can start out with mocks with hard coded returns,
but will probably refactor into more general
objects. - Ex Simulated Database Framework
26Acceptance Tests With Databases
- An acceptance test Want to test the "whole" app.
- Good for testing that the database really likes
the SQL we hardcoded in the unit tests, and
really responds the way we expect
27Acceptance Tests With Databases, Contd
- Big question is, how can we automate? I built up
a toolkit as I went. - BulkLoadData reads CSV files and loads data into
DB (use Excel to edit) - ExecuteSqlScript processes a text file of SQL
commands. - Used to create tables, etc.
- ExecuteDatabaseSetupScript allows me to write
little scripts - Knows about 4 commands, including BulkLoadData
and ExecuteSqlScript - TestResource framework
28Acceptance Tests With Databases, Contd
- Big question is, how can we automate? I built up
a toolkit as I went. - TestResource framework
- I can define test resources my test needs,
setup/teardown methods, and dependencies. - Resources will set themselves up from any initial
state (ex, delete all rows in table and reload) - Now, the acceptance test can just declare all the
resources it needs, and framework will set them
up. Just needs to mark which resources it
dirties, so they can be reset for subsequent
tests.
29Summary
- Preaching about TDD
- What is a Unit Test
- Common Unit Testing Patterns
- Unit Testing Database Interactions
- Acceptance Tests With Databases
- Questions / demos!