More Related Content Similar to Database Development: The Object-oriented and Test-driven Way (20) Database Development: The Object-oriented and Test-driven Way2. Max Guernsey
Hexagon Software, LLC
As head of Hexagon Software, LLC and a software developer with fifteen years of
experience, Max Guernsey helps organizations make the transition to agility with a focus on
good technical practices such as test-driven development and modern object-oriented design.
He develops his company’s DataClass product, a language that allows .NET developers to treat
databases like “just another object.” Max is the author of Transition Testing, the foundation of
his current TDD database work; Test Driven Database Development which focuses on adapting
the discipline of TDD to forces present in the database world; and Goad Testingin which he
discusses what unit tests really are and how to make them more resilient to design changes.
5. Assumptions
I assume that you know...
• I assume that you know
– ...know what TDD is
– ...the difference between TDD and Test‐First
– ...about mocking
– ...a little about design patterns
• If not, there are great resources for all
• Pick them up, read, & practice before talk
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
5
Know what’s in our way
Establish the Problem
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
6
3
6. TDD in Middle Tier Code
[Test]
public void AddEmployee() {
var company = Company GetInstance();
Company.GetInstance();
var employee = Employee.GetInstance(); Setup
Test Runner
Test Runner
Test1
company.AddEmployee(employee);
Trigger
Test2
Test3
Assert.That(
company.Employees,
Has.Some.SameAs(employee);
Assertion
ThisTest
Test5
}
Test6
Test7
Test8
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
7
TDD in Middle Tier Code
[Test]
public void AddEmployee() {
var company = Company GetInstance();
Company.GetInstance();
var employee = Employee.GetInstance(); Setup
company.AddEmployee(employee);
Trigger
Assert.That(
company.Employees,
Has.Some.SameAs(employee);
Assertion
}
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
8
4
7. Typical DB Test
[Test]
public void CannotAddLineItemWithoutType() {
DBTestHelper.ClearDatabase();
DBTestHelper ClearDatabase();
Incomplete setup
var cartId = DBTestHelper.CreateCart();
try {
DBTestHelper.InsertLineItem(
Trigger
cart: cartId, itemTypeId: DBNull.Value, count: 100);
} catch (ConstraintViolationException) {
return;
}
Assertion
Assert.Fail();
}
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
9
How Was the DB Created?
• You don’t know
• No guarantee DB works like production
• Potential for variation
• Leads to...
– ...rigorous “controls” over change
– ...general “slow down” culture
...general slow down culture
– ...ultimately: bad database designs
• DB test suites must cover creation
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
10
5
9. There’s More!
• OOD
– C ti
Creating systems of logical DBs w/good coupling & flexible designs
t
f l i l DB /
d
li & fl ibl d i
• Mocking
– Real, useful mocking along interface boundaries
• Legacy Databases
– Pulling legacy DBs into new process
• Non‐DB applications
– Serialized objects, XML, file structures
• Won’t fit in 75 minutes
• Shameless plug
– They’re in the book
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
13
You don’t want to run tests against production, do you?
Creating a Class of Databases
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
14
7
10. What is a Class of Databases?
• Strictly
St ct y
– Provisions new DBs
– Upgrades existing DBs
– All DBs get same behavior
• Effecti el
Effectively
– Uniform create/upgrade path
– All DBs “grew” same way
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
15
Problem: Different Build Paths
• Test databases need to be “cheap”
est databases eed to be c eap
– Easy to create & destroy
• Production databases need to last
– Created w/scripts
– Improved incrementally w/scripts
• Want all databases to ork the same
Want all databases to work the same
– Test & production
• Exactly the same
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
16
8
11. How to Make Test DBs?
• Test DBs are of low value
est s a e o o a ue
• Especially when cheap to make
• Break one?
– Pitch it
• Got one dirty?
– Pitch it
• Make them however you like
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
17
How to Make Production DBs?
•
•
•
•
•
•
Starts empty
Starts empty
Created w/series of instructions
Upgraded w/series of instructions to v2
Upgraded w/series of instructions to v3
Etc.
Etc
No wiggle room
– What actually happens is what actually happens
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
18
9
13. Example: Writing a Test
[Test]
public void HasAnAccountsTable() {
// you can do this once per suite if you want
MyDatabase.Create(connection, 2); DB created by test
Test Runner
Test Runner
Test1
connection.ExecuteSql("SELECT * FROM Accounts");
Test2
Test3
// exception thrown if no Accounts table
ThisTest
}
Test5
Test6
Test7
Test8
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
21
Example: Starting DB Script
<database>
<version number="1">
<script>
<!-- Build the database -->
</script>
</version>
</database>
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
22
11
14. Example: Updating DB Design
<database>
<version number="1">
<script>
<!-- Build the database -->
</script>
</version>
<version number="2">
<script>
<![CDATA[
CREATE Accounts(INT ID, NAME CHAR(23));
]]>
</version>
</database>
Test Runner
Test Runner
Test1
Test2
Test3
ThisTest
Test5
Test6
Test7
Test8
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
23
Reducing the risk of change rather than its frequency
Safely Changing Design
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
24
12
15. “Neat”
• Finding uniform build path a beginning
d g u o bu d pat a beg
g
• Must drive error/variation from build
• Builders/installers/etc. are code
• What drives error/variation from code?
• TESTS!!
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
25
Takeaway #2
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
26
13
16. A Transition Test
• Setup
– Bring database up to version X
– Populate
• Trigger
– Transition to subsequent version
• Assertion
– Prove that content was preserved
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
27
Example: A Transition Test
[Test]
public void ContactsFactoredFromAccountsInV9() {
MyDatabase.Create(connection, 8);
var accountId = connection.ExecuteScalar(@"
INSERT INTO Accounts(Name, PWHash, Email)
VALUES('MaxGuernseyIII', 'pwhash', 'max@hexsw.com');
SELECT SCOPE_IDENTITY();");
Test Runner
Test Runner
Test1
Test2
Test3
MyDatabase.Create(connection, 9);
ThisTest
var emails = connection.ExecuteSelectList(@"
SELECT Email FROM Contacts WHERE AccountID = ?",
accountId);
Assert.That(
emails,
Is.EqualTo(new[] { "max@hexsw.com" });
Test5
Test6
Test7
Test8
}
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
28
14
17. Example: Creating Upgrade Script
<database>
<version number="9">
<script>
p
<![CDATA[
CREATE TABLE Contacts(
AccountId INT FOREIGN KEY REFERENCES Account(ID),
Email NVARCHAR(2048));
Test Runner
Test Runner
Test1
Test2
Test3
INSERT INTO Contacts(AccountID, Email)
SELECT ID, Email FROM Accounts;
ThisTest
Test5
ALTER TABLE A
Accounts DROP COLUMN E il
t
Email;
]]>
</script>
</version>
</database>
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
Test6
Test7
Test8
29
Getting the best feedback you can
Enforcing Interfaces
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
30
15
18. Remember This?
[Test]
public void HasAnAccountsTable() {
// you can do this once per suite if you want
MyDatabase.Create(connection, 2);
connection.ExecuteSql("SELECT * FROM Accounts");
// exception thrown if no Accounts table
}
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
31
Duplication Is Evil
•
•
•
•
Two tests is annoying
Two tests is annoying
Ten is a pain
Fifty is untenable
Any realistic number impossible to maintain
w/duplication
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
32
16
19. Easy Solution
[Test]
public void HasAnAccountsTable() {
// you can do this once p
y
per suite if y
you want
MyDatabase.Create(connection, MyDatabase.CurrentVersion);
connection.ExecuteSql("SELECT * FROM Accounts");
// exception thrown if no Accounts table
}
...
public class MyDatabase {
public const int CurrentVersion = 2;
...
}
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
33
It’s More Pervasive
Version numbers just most obvious
• Version numbers just most obvious
• Symbols are the real evil...
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
34
17
20. Duplication Abounds
[Test]
public void CanInsertData() {
connection.Execute(@"INSERT INTO Data(Time, Reading)
VALUES( 4:31:01.103 17.02.2009
VALUES('4:31:01 103 17 02 2009', 041 908);”);
041.908); );
}
...
<script>
CREATE TABLE Data(Time DATETIME, Reading NUMERIC(6,3));
</script>
...
private void EnterReading(double reading) {
connection.Execute(
@"INSERT INTO Data(Time, Reading) VALUES(?,?)",
DateTime.Now, reading);
}
...
private IEnumerable<double> GetOrderedReadings() {
return connection.Execute<double>(@"SELECT Reading FROM Data
ORDER BY Time ASC");
}
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
35
One Solution
Minimize w/string constants
• Minimize w/string constants
• Couple to constants from tests & code
• What about interface changes?
– Transition tests need old design
Transition tests need old design
– Unit tests & production code always need latest
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
36
18
21. Another Solution
•
•
•
•
Document design w/each transition
Document design w/each transition
Couple transition tests to exact
Couple unit tests to latest
Couple production code to latest
• What about DB script?
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
37
A Really Good Solution
• Document design changes w/version
ocu e t des g c a ges / e s o
• Bind to design in scripts
• Pre‐compile/package step:
– Inject bound symbols
– Generate coupling code
• C
Couple unit tests to latest design
l
i
l
d i
• Couple production code to latest design
• Couple transition tests to specific design
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
38
19
23. Replacing Symbol w/Itself?
• 1st‐ly: You can do more
y ou ca do o e
– E.g.: Use different DB and logical names
• 2nd‐ly: Gives advantages of compiler
– across languages (SQL, C#, C++, etc.)
• Don't believe compiler has advantages?
' b li
il h
d
?
– You're crazy
– Keep it to yourself
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
41
If databases are objects, what are their behaviors?
Defining Behaviors
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
42
21
25. What Are Behaviors for
Transitions?
• Transition behaviors simple
Transition behaviors simple
• Change from old structure to new
• Preserve knowledge
• Ignored for this segment
Ignored for this segment
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
45
What Are DB Behaviors?
DB behaviors slightly more subtle
• DB behaviors slightly more subtle
• Absorb information & translate to knowledge
• Translate knowledge into requested info.
• A table is no behavior
A table is no behavior
• A trigger is not behavior
• Those are structure
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
46
23
27. Don't and Do
Don't
• Test a table
• Test a trigger
• Expose structure
– E.g.: tables etc.
9/12/2012
Do
• Test store & retrieval of info.
• Test info causes side effect
• Encapsulate structure
– w/Views & stored procs
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
49
Why Encapsulate Design?
Weird question: Why not?
• Weird question: Why not?
• Hidden things cheap to change
• Exposed ones expensive
• Minimizing public interface maximizes
Minimizing public interface maximizes
flexibility
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
50
25
29. Plan for the Future
Plan for the future to happen
• Plan for the future to happen
– You'll need to change stuff
• Design so that change is easiest
• Largely: what's been shown so far
Additionally: don t overbuild
• Additionally: don't overbuild
• "What does that mean?"
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
53
Takeaway #5
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
54
27
30. Planning for the Future
• Encapsulate design
– So that structures can change
• Enforce interface strongly
– So changes don't break downstream functionality
• Cover every behavior w/test
– So changes don't introduce regression
• Cover every transition w/test
y
/
– So changes don't jeopardize knowledge
• Minimize features to those needed now
– So needed features not impeded by useless ones
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
55
Time to watch this work
Live Example
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
56
28
32. Mocking
Once you have OOD/OOP, easy, good mocking
• Once you have OOD/OOP easy good mocking
available
• Don't mock tables
• Mock behaviors encapsulated in dependency
DBs
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
59
Legacy DBs
• Mocking especially handy w/legacy DBs
oc g espec a y a dy / egacy s
• Create Façade DB, connects to legacy
• Easy to test‐drive Façade behaviors:
– Just mock out legacy
• Transfer behavior to Façade over time
• Alternatively:
– "wrangle" legacy DB into tested state
9/12/2012
© Copyright 2011 Hexagon Software LLC.
All rights reserved.
60
30