TDD involves writing tests before writing code to satisfy requirements. The document discusses TDD, providing:
1. An overview of the TDD process and definitions of its key steps - make a test, make it fail, make it pass.
2. An example walking through writing a test for an "easy button" and implementing the code to pass the test.
3. Reasons for using TDD, including improved code quality, design, discipline, and documentation from maintaining an automated test suite.
08448380779 Call Girls In Civil Lines Women Seeking Men
Apply TDD to SQL with .NET
1. Hey, You Got
Your TDD
In My SQL DB!
Jeff McKenzie
@jeffreymckenzie
mail@mcknz.com
Good morning everyone –
Thanks for being here to learn
About SQL Server testing using TDD
My name is Jeff McKenzie, and before we get started
I'd like to tell you a little bit about myself –
I like puppies, long walks on the beach…
And I'm also a Principal Consultant
At Cardinal Solutions – a little bit about Cardinal...
2. Cloud Mobile
Data Web
We are an IT services firm
With approximately 500 fulltime consultants
In 7 different locations across the eastern region,
Including a branch in Columbus –
And we are hiring, so talk to me after
Or get in touch if you are interested --
We have a strong focus in cloud technologies,
As well as custom application development,
DevOps, IoT, AR/VR, and data analytics.
3. @jeffreymckenzie
TDD
Today we are going to learn more about TDD –
How many of you have heard of TDD,
know what it means, or tried it? [hands]
How many use TDD
on a fairly regular basis? [hands]
4. @jeffreymckenzie
How to apply
TDD
to SQL Server
So My goal for today is to show you
How to use TDD practices
Specifically in SQL Server,
Whether that's standalone SQL,
Or the data layer in a larger application. ***
5. @jeffreymckenzie
TDD – the what
1
To realize that goal, we are first going to
Define TDD, explain it, qualify it,
To make sure we are all on the same page
When we talk about what TDD is and is not. ***
6. @jeffreymckenzie
TDD – the why
2
A lot of us have heard about the practice of TDD
But sometimes the actual reasons for doing it
get lost or obscured.
So we will examine the benefits of TDD. ***
7. @jeffreymckenzie
TDD – the how
3
Finally we will take a look at a specific example
Of a real-life production scenario
And demonstrate how to use TDD
against SQL Server. ***
8. @jeffreymckenzie
I Don't Know.
So one thing I'm not planning to do in this session
Is try to convince you how often and how thoroughly
you apply TDD, because I don't really have an answer.
This is one of those software development topics that
Tend to start religious wars, like, you know,
Other enthralling topics like spaces vs tabs.
On one side you have the TDD zealots… ***
=========
https://upload.wikimedia.org/wikipedia/commons/7/7b/Jan_Steen_-
_Argument_over_a_Card_Game_-_WGA21735.jpg
Jan Steen [Public domain], via Wikimedia Commons
9. @jeffreymckenzie
All
the
Way
Down
Who say even your unit tests should have unit tests.
On the other side are people who say TDD
Is garbage, is considered harmful, ***
=========
https://upload.wikimedia.org/wikipedia/commons/4/47/River_terrapin.jpg
By Pelf at en.wikipedia [Public domain], via Wikimedia Commons
10. @jeffreymckenzie
Burn It With Fire.
Promotes bad design, and should be
completely done away with.
Those are ideas and arguments you can explore
on your own.
All I can say is that I've used it, ***
=========
https://upload.wikimedia.org/wikipedia/commons/c/cd/Rammsterin_Fla
methrower.jpg
By Kreepin Deth (Own work) [CC BY-SA 4.0
(https://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia
Commons
11. @jeffreymckenzie
Works on My Box.
Found some benefits with it, and generally have a feel
For when it's valuable and when it's not.
But all those concerns are offtopic for today –
We will be focusing on the what, why, and how. ***
=========
https://upload.wikimedia.org/wikipedia/commons/f/f5/Computer_MC80.3
x_CM5214.JPG
By LutzBruno (Own work) [CC BY 4.0
(http://creativecommons.org/licenses/by/4.0)], via Wikimedia Commons
12. @jeffreymckenzie
Who
Are
You
First, I'd like to get an idea of your technical background:
How many would consider yourselves
…application developers?
…DBAs or primarily SQL developers?
…QA or testing?
Any roles I missed? ***
=========
https://upload.wikimedia.org/wikipedia/commons/4/42/Townshend_sma
shing_guitar.jpg
By Heinrich Klaffs [CC BY-SA 2.0
(https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia
Commons
13. @jeffreymckenzie
Test-driven development was popularized in software development
In the late 1990s as part of the Extreme Programming project,
Which emerged as a set of technical practices designed to work with
Agile Software Development.
The specific technique of TDD was detailed in Kent Beck's
2002 book Test-Driven Development by Example.
=========
https://www.amazon.com/Test-Driven-Development-Kent-
Beck/dp/0321146530
https://upload.wikimedia.org/wikipedia/commons/5/55/Kent_Beck_no_
Workshop_Mapping_XP.jpg
By Improve It (Flickr: Kent Beck no Workshop Mapping XP.) [CC BY-SA
2.0 (https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia
Commons
14. @jeffreymckenzie
The original description of TDD was in
an ancient book about programming. It
said you take the input tape, manually
type in the output tape you expect, then
program until the actual output tape
matches the expected output.
– Kent Beck
In that book however, Beck claims merely to have
re-discovered the technique. He cites what he calls
an ancient book on programming
(ancient in software development terms, which means
late sixties/Early seventies), but references have been
Discovered going back to the 50s. ***
=========
https://www.amazon.com/Test-Driven-Development-Kent-
Beck/dp/0321146530
https://upload.wikimedia.org/wikipedia/commons/5/55/Kent_Beck_no_
Workshop_Mapping_XP.jpg
By Improve It (Flickr: Kent Beck no Workshop Mapping XP.) [CC BY-SA
2.0 (https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia
Commons
15. @jeffreymckenzie
The original description of TDD was in
an ancient book about programming. It
said you take the input tape, manually
type in the output tape you expect, then
program until the actual output tape
matches the expected output.
– Kent Beck
Beck describes a process where the expected output
of the program was manually created,
and the programmer would work until the
actual output matched the expected output,
Which is pretty much what TDD is all about. ***
=========
https://www.amazon.com/Test-Driven-Development-Kent-
Beck/dp/0321146530
https://upload.wikimedia.org/wikipedia/commons/5/55/Kent_Beck_no_
Workshop_Mapping_XP.jpg
By Improve It (Flickr: Kent Beck no Workshop Mapping XP.) [CC BY-SA
2.0 (https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia
Commons
16. @jeffreymckenzie
TDD – the what
1
We call this practice Test-Driven
Because tests are written first, before any code.
With a traditional approach, tests are created after,
To verify what's already been coded. ***
17. @jeffreymckenzie
Don't know about you, but I find it helpful
to have a visual of what I'm trying to learn.
Here's a graphical depiction of the TDD process –
Which is really two separate cycles:
The test-driven development cycle,
And the refactoring cycle.
There are 3 steps for the first cycle:
Make the test, make it fail, and make it work. ***
=========
https://upload.wikimedia.org/wikipedia/commons/0/0b/TDD_Global_Life
cycle.png
By Xarawn (Own work) [CC BY-SA 4.0
(https://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia
Commons
18. @jeffreymckenzie
Make the Test.
First step is probably the hardest barrier to get over
when learning and using TDD, and that is to create
the test first, before writing any code.
When we get requirements, we want to make
something happen, And writing a test
doesn't really feel like you're doing anything.
I'll add another point about writing a test, ***
=========
https://upload.wikimedia.org/wikipedia/commons/b/b2/V08383P339.jpg
By Calspan Corporation, National Highway Traffic Safety Administration
[Public domain], via Wikimedia Commons
19. @jeffreymckenzie
Make it simple.
Which is to make it simple. A test shouldn't try
to do too much.
It should be relatively easy to create and execute.
The tests we are talking about here are unit tests.
There's a definition for unit test I like,
Which says it's
One specific requirement for
One specific method. ***
=========
Picture of easy button, by me.
20. @jeffreymckenzie
…a test that tests
ONE specific requirement for
ONE specific method.
A Unit Test is…
– James Bender
This is from James Bender – just google James Bender
And TDD, and you'll find a lot of great info.
This definition really shows the difference between
a unit test and any higher level test, such as
integration or end-to-end testing.
These higher-level tests are going to hit
multiple methods, which makes it harder
To verify individual requirements.
A unit test should also be…. ***
21. @jeffreymckenzie
…fast
…isolated
…repeatable
A Unit Test is…
Fast.
Ideally you want to run all of your tests
as you are working,
To know when you are done
or have broken something.
If your tests are simple,
they are more likely to be fast. ***
22. @jeffreymckenzie
…fast
…isolated
…repeatable
A Unit Test is…
Your tests will also be faster
If they are isolated – meaning you want to reduce
Dependencies on other classes or methods.
Testing methods in isolation also makes sure
That you're targeting the right spot –
If your test fails, you know
Exactly where to look. ***
23. @jeffreymckenzie
…fast
…isolated
…repeatable
A Unit Test is…
Finally a test should be repeatable –
So if the code being tested has not changed,
The test result should be the same every time.
Any setup or teardown activity
should be handled within the test itself.
There should be no manual steps required
In between test runs.
This again helps the test to run quickly. ***
24. @jeffreymckenzie
Let's try out a quick concrete example.
So we have our easy button, and let's say
We want to create a computer model of it.
Our first requirement is…
Create an easy button,
=========
Picture of easy button, by me.
25. @jeffreymckenzie
Create an Easy Button…
with a Click() method…
returning “That was easy.”
With a click method,
Returning “That was easy.”
***
26. @jeffreymckenzie
public void Test_EasyButton_Click() {
//Arrange
//Act
//Assert
}
Here's the start of our test – we'll call it
Test Easy Button Click – and we're going to use
What we call the triple A pattern:
Arrange, Act, Assert.
Let's set up the arrange part –
This is where you do all of the setup
Just prior to code execution.
27. @jeffreymckenzie
public void Test_EasyButton_Click() {
//Arrange
var button = new EasyButton.Button();
String expected = "That was easy.";
//Act
//Assert
}
This is C# but this would be similar to a test
In a strongly typed language.
Here we create an instance of the EasyButton,
And also a variable called expected,
Which holds the value of the output
we expect. ***
28. @jeffreymckenzie
public void Test_EasyButton_Click() {
//Arrange
var button = new EasyButton.Button();
String expected = "That was easy.";
//Act
String actual = button.Click();
//Assert
}
Next we have the Act section –
Here we do the actual code execution,
And assign the output of the click method
To a variable called actual. ***
29. @jeffreymckenzie
public void Test_EasyButton_Click() {
//Arrange
var button = new EasyButton.Button();
String expected = "That was easy.";
//Act
String actual = button.Click();
//Assert
Assert.AreEqual(actual, expected);
}
Lastly we have our assert section.
An assertion is a verification
That our action executed the way we intended.
In this case, our assertion is that the actual output
of the click method is: That was Easy.
This is a pretty simple test –
We are testing just one thing. ***
30. @jeffreymckenzie
Make it fail.
After you've written your simple test,
You want to make sure it actually fails.
If you write a passing test by mistake,
Then you'll have no confidence that
Your code really does what it's supposed to.
Then, finally, write the code… ***
=========
https://upload.wikimedia.org/wikipedia/commons/5/54/Hydrogen_balloo
n_explosion.jpg
By Maxim Bilovitskiy (Own work) [CC BY-SA 4.0
(https://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia
Commons
31. @jeffreymckenzie
public void Test_EasyButton_Click() {
//Arrange
var button = new EasyButton.Button();
String expected = "That was easy.";
//Act
String actual = button.Click();
//Assert
Assert.AreEqual(actual, expected);
}
So back to our test – is it going to pass?
No – why not?
Right, we haven't written the code yet,
There's no EasyButton.
Here's what I get when I attempt
To run the test … ***
32. @jeffreymckenzie
It doesn't even compile.
Tells me that EasyButton doesn't exist,
Which is good because
We haven't made it yet. ***
33. @jeffreymckenzie
Make it work.
So we need to make the test work.
The trick is to try to write as little code as possible.
The idea of TDD is to satisfy all requirements
With the least amount of work.
The less code there is, the fewer things
You have to go wrong.
So let's write the implementation. ***
=========
https://upload.wikimedia.org/wikipedia/commons/c/c4/Hillary_Clinton_%
2824007578223%29.jpgBy Gage Skidmore from Peoria, AZ, United
States of America (Hillary Clinton) [CC BY-SA 2.0
(https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia
Commons
34. @jeffreymckenzie
public class Button {
public String Click() {
return "That was easy.";
}
}
Pretty simple –
We just create the class, Create the method,
And return the output. ***
35. @jeffreymckenzie
So we re-run our test and we have success.
We coded only what we needed to pass the test.
If we code ahead and try to anticipate
Additional requirements or functionality,
we run the risk of coding too much, or adding code
We don't need. ***
36. @jeffreymckenzie
Now we get to refactoring, which is simply
rewriting code to make it better.
After you have a few passing tests,
You'll want to go back and make improvements.
The nice thing about having TDD is you now
Have a built-in test suite to verify that
All of your changes still meet the requirements.
If tests fail, you may need to fix code,
Or you may need to update tests to match
Your new design. ***
=========
https://upload.wikimedia.org/wikipedia/commons/0/0b/TDD_Global_Life
cycle.png
By Xarawn (Own work) [CC BY-SA 4.0
(https://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia
Commons
37. @jeffreymckenzie
TDD – the why
2
Now that we have a general idea of what TDD is,
Let's examine why we might want want to use it –
What are the benefits it provides.
After all, there is extra work involved –
It simply takes more time to write a test
For every requirement, and to continually run
And update those tests.
It's like having a second code base. ***
38. @jeffreymckenzie
…quality
…design
…discipline
…documentation
Use TDD for…
The first benefit of TDD is that it improves code quality.
If you are diligent in your testing effort,
And have a unit test for the classes and methods
That make up each requirement, you will have baked in
Verification of your application's functionality.
Because you have been checking for correctness
As you go, the code is better, and has less defects.***
39. @jeffreymckenzie
…quality
…design
…discipline
…documentation
Use TDD for…
Another area TDD can assist is in the design
And organization of your code. Because you writing
The least possible code, and tests first, it forces you
To think in advance – as in our EasyButton example:
We had to think about the implementation we wanted
When writing the test.
40. @jeffreymckenzie
…quality
…design
…discipline
…documentation
Use TDD for…
TDD can also improve testing discipline.
I know from experience that it's difficult to write
All the tests you need when you do it after the code.
And often it just doesn't happen because you
Run out of time. If you write the test first,
You'll rarely be in a place where you have untested code.
41. @jeffreymckenzie
…quality
…design
…discipline
…documentation
Use TDD for…
If you are disciplined in writing tests,
Your test suites become in essence a form
Of documentation for your code –
Each test explains what a part of the application does.
Written documentation quickly gets out of date,
But if you have a passing test, you know the
Code is still used and working as intended.
But my number one reason for using TDD is
That it gives you confidence to make changes. ***
42. @jeffreymckenzie
…confidence to change
code details and design
– and at the same time –
retain existing functionality.
Use TDD for…
You are continually rerunning old tests, so you'll know
immediately if something's broken.
I think everyone's had an experience working with
a code base that people are afraid to touch.
Which means at release time, everyone in the company
Is staying late to click on buttons, hoping
That nothing got broken since the last deployment. ***
43. @jeffreymckenzie
What Have I Done.
You know, everyone lives in fear of changes to the application,
And the managers want to know exactly what changed
That caused this feature or that feature to break,
And why didn't we know that would happen.
So TDD can help prevent these types of problems. ***
=========
https://upload.wikimedia.org/wikipedia/commons/7/7e/US_Navy_03022
8-N-4953E-
001_Aviation_Ordnanceman_2nd_Class_Kyle_Marcinowski_conducts_
a_quality_assurance_check_on_a_GBU-
31_Joint_Direct_Attack_Munition_%28JDAM%29.jpg
By U.S. Navy photo by Photographer's Mate 3rd Class Danny Ewing Jr.
[Public domain], via Wikimedia Commons
44. @jeffreymckenzie
TDD – the how
3
So that brings us to the heart of today's session,
Which is how to bring the benefits of TDD
To your SQL Server database. This brings up the question
How do we, and how should we, test our data? ***
45. @jeffreymckenzie
How Do
We Test
Our Data?
What are some ways you approach data testing?
I think that sometimes when testing an application,
our first inclination is simply to skip data all together,
Because the database is a external dependency that
Slows down testing, and can cause unrelated issues.
46. @jeffreymckenzie
Isolation
Remember we talked about isolation as being
An important part of a unit test?
If we are writing our application,
It's often best practice to isolate our code
From the data because it's an outside dependency.
But that presents a problem -- we have database objects
As part of our code base, but not testing them
Along with the application. ***
47. @jeffreymckenzie
Manual
Queries
One common way is to manually query the database.
We write a query or stored proc, run it,
and verify its correctness. And that works to a degree,
but eventually you have the same problem –
as your data model changes,
How can you tell you're still getting proper data?
You have to go back and manually retest,
Which often you don't have time for.
Another method is to build in automation
Through integration tests. ***
48. @jeffreymckenzie
Automated
Integration
Tests
These kinds of tests are certainly a step up
From manual queries, and can be built in
As part of a continuous integration process
To make sure the application works when combined
With its persistence layer. One problem with this,
However, is that these tests are designed to test
The application itself, and the data only indirectly.
Could be that a integration test only succeeds
Coincidentally, and that the underlying SQL
Has unidentified problems. ***
49. @jeffreymckenzie
When Do
We Test
Our Data?
In addition to how we test, we need to ask
When we test data. We don't need to test
Everything. For example, in an application,
We probably don't need to test
That an object property has the value we assigned it.
We want to make sure we are testing functionality,
And not the underlying framework or language.
We probably don't need to test an insert statement.
We probably don't need to test our ORM. ***
50. @jeffreymckenzie
When Do
We Test
Our Data?
What is useful to test is complex behavior or logic.
Although stored procedures are kind of out of fashion,
They are still a great choice when doing intensive
Operations within multiple database objects.
Wouldn't it be nice if we could build in
The same kind of direct and continuous
Unit testing of our SQL, the way we do
For our application code?
Well, it just so happens that we can! ***
51. @jeffreymckenzie
There is a unit test framework for SQL Server
Called T SQL T, which allows you to take a TDD
Approach to writing SQL.
It's installed entirely within SQL server.
It's open source, with the source code freely
Available on GitHub. ***
53. @jeffreymckenzie
On the download page at tSQLt.org
The whole framework clocks in zipped at 84K,
So that should pull down pretty fast… ***
54. @jeffreymckenzie
What we're going to walk through
Is a real world production feature I implemented
In SQL Server using the tSQLt framework.
In the interest of protecting client confidentiality,
however, I have changed almost everything
About the project, including the industry,
The client, and the product – all that remains
Is the business problem we need to solve.
So I'd like to introduce you to our client… ***
55. @jeffreymckenzie
Does anyone know this guy?
Ron was probably best known for being the director
Of the Parks and Rec department in Pawnee Indiana,
For 6 years. What's less well known, however, is that
after retiring from a long and illustrious career
as a civil servant, Ron Swanson decided to purchase
and run his favorite store… called… anyone? ***
=========
https://vignette.wikia.nocookie.net/parksandrecreation/images/0/06/Foo
d_and_Stuff_2.png/revision/latest?cb=20120730155117
http://parksandrecreation.wikia.com/wiki/File:Food_and_Stuff_2.png
56. @jeffreymckenzie
Food and Stuff. Does anyone remember this place?
Food and Stuff is located at 729 Glenmore Blvd
in Pawnee, Indiana. Ron buys all of his groceries there,
and describes it as: "a discount food outlet equidistant
from my home and my work". According to Mr. Swanson,
They have a broad and diverse catalog of items,
Including household paints…
=========
http://parksandrecreation.wikia.com/wiki/Food_and_Stuff
https://vignette.wikia.nocookie.net/parksandrecreation/images/1/15/Foo
d_and_Stuff.png/revision/latest?cb=20120730155051
59. @jeffreymckenzie
buckets of different sizes…..
=========
https://upload.wikimedia.org/wikipedia/commons/6/65/Pvc_cevi.jpg
Mm.zaletel from sl [GFDL (http://www.gnu.org/copyleft/fdl.html)], via
Wikimedia Commons
63. @jeffreymckenzie
And it's not only products –
They perform various services,
Such as engine repair…
=========
https://upload.wikimedia.org/wikipedia/commons/6/69/LeadPaint1.JPG
By Thester11 (Own work) [CC BY 3.0
(http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons
67. @jeffreymckenzie
So Ron's been doing pretty well, and business is strong.
He's got a point of sale system he's been using…
***
=========
https://vignette.wikia.nocookie.net/parksandrecreation/images/0/06/Foo
d_and_Stuff_2.png/revision/latest?cb=20120730155117
http://parksandrecreation.wikia.com/wiki/File:Food_and_Stuff_2.png
68. @jeffreymckenzie
Yeah, So no one ever accused Ron
of being a slave to technology.
He's also using this system to take customer orders,
track inventory, manage his books,
and all that good stuff.
But Ron has a few problems.
***
=========
https://upload.wikimedia.org/wikipedia/commons/1/19/Zenith_Z-
19_Terminal.jpg
By Jamie Cox from Melbourne, USA (Zenith Z-19 Terminal Uploaded
by Mewtu) [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)],
via Wikimedia Commons
69. @jeffreymckenzie
First, he's got about 12,000,000 new customers in his database
over the last 12 months, but the entire city of Pawnee
has only about 80,000 people.
=========
http://1.bp.blogspot.com/-
j9CbeOiNMLY/Vdr4b55bzcI/AAAAAAAAwOM/A_fjFcEBSJo/s1600/Paw
nee.jpg
https://swimnova.com/map-of-pawnee-indiana.html
70. @jeffreymckenzie
When he runs customer reports, he keeps seeing
the same names appear many times.
The point of sale system also allows Ron to mail
promotional materials to his customers, to remind
them when there are sales, or when new items
get in stock, like almond butter….
=========
https://upload.wikimedia.org/wikipedia/commons/3/3a/All_work_and_no
_play_makes_Jack_a_dull_boy_%28The_Shining%29_%28795773850
0%29.jpg
By Marcel Oosterwijk from Amsterdam, The Netherlands [CC BY-SA
2.0 (https://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia
Commons
72. @jeffreymckenzie
But he's gotten complaints that his customers
are getting 4 or five copies of his newsletter,
and he's spending a fortune on postage.
So what is Ron's problem?
What's the problem with his system,
and specifically his database? ***
=========
https://upload.wikimedia.org/wikipedia/commons/d/d3/Electric_cattle_pr
od.jpg
Author Unknown
73. @jeffreymckenzie
Duplicate
Data
Yes, it's duplicate data.
Why might he have some duplication?
[doesn't check for existing customers]
[no customer search]
[no DB cleaning]
So Ron has asked us, all of us here,
To solve his problem, and we, collectively,
Are known as Apps N Stuff. ***
74. @jeffreymckenzie
Apps 'N Stuff
Ron wants us to fix his duplicate data problem.
He has 2 basic requirements for us.
First, he doesn't want to have to check
For duplicates up front.
75. @jeffreymckenzie
1. No Check at Order Time
2. Self-Service Cleanup
He really doesn't want the UI
for his order process to change
(you know, he likes things the way they are)
And he's worried that searching up front
Will slow the order, and that will make
His customers unhappy.
Second, he wants to clean up duplicates himself.
76. @jeffreymckenzie
1. No Check at Order Time
2. Self-Service Cleanup
He doesn't want Apps 'N Stuff to come out
Every week or so and clean up the data.
He also doesn't want to have some
Batch process running in the background
To automatically clean things up,
Because well, he just doesn't trust it.
77. @jeffreymckenzie
Food And Stuff
Solution
So we have to build a solution
That allows Ron to access the data,
Find duplicates,
And merge them together,
into a single record.
The first part of the solution is
a UI component,
78. @jeffreymckenzie
The actual screen that lets Ron find duplicates.
The second part of the solution
Is the backend database -- ***
=========
https://upload.wikimedia.org/wikipedia/commons/e/e0/Browser_ballonic
on2.svg
By pixelbuddha [CC BY 3.0
(http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons
79. @jeffreymckenzie
This is where we are going to focus
Our effort today.
We are going to write a stored procedure
That takes 2 duplicate customer records, ***
=========
https://commons.wikimedia.org/wiki/File:Applications-database.svg
By dracos (http://dracos.deviantart.com/#/d2y5ele) [CC BY-SA 3.0
(https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia
Commons
80. @jeffreymckenzie
Ron Swanson
Ron
Swanson
Ronald
Swanson
And merges them into a single customer.
The UI is going to take care of finding the customers,
So all the proc has to do is take the two records
And combine them. But there are complications.
We also have to make sure that the order history
For each customer is combined as well.
Let's take a look at the data model. ***
=========
https://upload.wikimedia.org/wikipedia/commons/4/40/Data-transfer.svg
By RRZEicons (Own work) [CC BY-SA 3.0
(https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia
Commons
81. This is an extremely simplified model –
I would not recommend this for a production DB.
Customer = first name, last name, loyalty id
OrderDetail = item purchased
Order = joins Customer with an Order
So once we are done, we should be left
With a single customer who has all the orders,
And the duplicate customer deleted. ***
82. @jeffreymckenzie
Before we start writing our procedure, however,
We need to get our TDD framework set up.
The tSQLt framework is very small, and in fact,
Contains only 6 files. ***
***
83. @jeffreymckenzie
There's a text file that explains the setup,
The license and release notes, and some example tests.
To install tSQLt you need to run the
Set CLR Enabled script –
There are some compiled assemblies
that tSQLt uses –
And then execute the tSqlt.class script,
Which will install all of the DB objects
That the framework needs. ***
84. @jeffreymckenzie
And what this does is install all
Of the tables, procedures, and functions
In its own schema, the tSQLt schema,
So it's logically separated from
The dbo schema or
Anything else you've created.
Now that we have tSQLt installed,
We can start working on our tests. ***
85. @jeffreymckenzie
1. Create Test Install Script
2. Create Test Run Script
So we are going to do two things –
First, create a SQL Script that installs our tests
in the database,
And second, create a SQL Script
that runs our tests.
Let's look at the install script first. ***
86. @jeffreymckenzie
USE Food_And_Stuff
GO
EXEC tSQLt.NewTestClass
@ClassName = N'FAS_Tests'
GO
------------- BEGIN [FAS_Tests].[InsertTestData] ----------
CREATE PROC FAS_Tests.InsertTestData
AS
BEGIN
END
------------- END [FAS_Tests].[InsertTestData] ------------
First, we're going to make sure that everything
Gets installed in the Food_And_Stuff database
With the use statement.
To execute a tSQLt statement, you type the
Schema name, followed by the command you want.
Here we use the tSQLt New Test Class command
To create our test class – what this actually does
Is create a new schema in the database we can
Attach our tests to…
87. @jeffreymckenzie
USE Food_And_Stuff
GO
EXEC tSQLt.NewTestClass
@ClassName = N'FAS_Tests'
GO
------------- BEGIN [FAS_Tests].[InsertTestData] ----------
CREATE PROC FAS_Tests.InsertTestData
AS
BEGIN
END
------------- END [FAS_Tests].[InsertTestData] ------------
again, this provides
A clear separation between our actual code,
Our tests, and the tSQLt framework.
One note of warning –
this will delete any test class with that name,
Without any warning (so script everything!)
Next we will create a procedure that inserts
The data we need for our tests.
First we'll create data for our customer table.
88. @jeffreymckenzie
---------------- BEGIN CUSTOMERS -------------------
DECLARE @RonaldId INT = 1, @RonId INT = 2
INSERT INTO dbo.Customer(
CustomerId, FirstName, LastName, LoyaltyId
)
VALUES(@RonaldId, 'Ronald', 'Swanson', 1234)
INSERT INTO dbo.Customer(
CustomerId, FirstName, LastName, LoyaltyId
)
VALUES(@RonId, 'Ron', 'Swanson', 1234)
---------------- END CUSTOMERS ---------------------
We want to test that a duplicate gets eliminated,
So we'll need two customers.
We're going to say Ron Swanson is the customer
We want to keep, and Ronald Swanson is the duplicate
So we'll insert customer ids (Ron is number 2),
The first name and last name, and finally the loyalty ID.
The loyalty ID is what tracks who gets what discounts --
89. @jeffreymckenzie
---------------- BEGIN CUSTOMERS -------------------
DECLARE @RonaldId INT = 1, @RonId INT = 2
INSERT INTO dbo.Customer(
CustomerId, FirstName, LastName, LoyaltyId
)
VALUES(@RonaldId, 'Ronald', 'Swanson', 1234)
INSERT INTO dbo.Customer(
CustomerId, FirstName, LastName, LoyaltyId
)
VALUES(@RonId, 'Ron', 'Swanson', 1234)
---------------- END CUSTOMERS ---------------------
You know, it's that card on your keychain
That says – yes, please take all of my personal
Information and record my purchases so
You can wrap it all up in a nice bow to sell
To a data broker who can in turn sell it
To an advertiser so they can milk me for more cash.
But I'm not cynical or anything.
But thank goodness Food And Stuff has this
Loyalty ID, because otherwise we'd have no way
To uniquely identify customers.
90. @jeffreymckenzie
---------------- BEGIN ORDERS ----------------------
DECLARE @RonaldOrderId INT = 11, @RonOrderId INT = 22
-- customer 1 order
INSERT INTO [dbo].[Order](OrderId, CustomerId)
VALUES(@RonaldOrderId, @RonaldId)
-- customer 2 order
INSERT INTO [dbo].[Order](OrderId, CustomerId)
VALUES(@RonOrderId, @RonId)
---------------- END ORDERS ------------------------
We'll do the same for orders,
Creating an order ID for both Ron and Ronald –
Again, at the end of this process,
Ron should have 2 orders under his name.
91. @jeffreymckenzie
---------------- BEGIN ORDER DETAIL ----------------
DECLARE @RonaldDetailId INT = 111
DECLARE @RonDetailId INT = 222
INSERT INTO [dbo].[OrderDetail](OrderDetailId, OrderId, ItemName)
VALUES(@RonaldDetailId, @RonaldOrderId,
'T-Bone Steaks: Val-U 20 Pack')
INSERT INTO [dbo].[OrderDetail](OrderDetailId, OrderId, ItemName)
VALUES(@RonaldDetailId, @RonaldOrderId,
'Bacon Strips: Val-U 100 Pack')
---------------- END ORDER DETAIL -------------------
And finally for order details.
We can see here that Ronald bought
A Value pack of 20 T-Bone steaks,
And Ron bought a Value pack
Of 100 bacon strips.
Clearly this is the same person.
92. @jeffreymckenzie
Fakes / Mocks
For the next part we need to talk about
The concept of faking or mocking in tests.
This is all part of test isolation that I
mentioned before. If the particular method
we are testing has to make a database
Or webservice call, we can simply create
A fake or mock of that call to keep our tests
Simple and focused. ***
93. @jeffreymckenzie
public class Button {
public String Click() {
return "That was easy.";
}
}
Let's go back to our EasyButton example.
Remember we implemented our click method?
Let's say instead of returning a hardcoded string,
We want to look up the quote from a service.
So let's do the replacement. ***
94. @jeffreymckenzie
public class Button {
public String Click(MessageService) {
return MessageService.Get();
}
}
I'm using a little pseudocode here just to
Make things a little clearer.
Now our unit test will actually be testing
The Click method and the service –
This will take longer, and could result
In failures from the service that are
Unrelated to our actual click method.
So what we can do is create a fake
Of our Service call. ***
95. @jeffreymckenzie
public class FakeMessageService {
public String Get() {
return “When I eat,
it is the food
that is scared.
– Ron Swanson";
}
}
Instead of passing a real instance
Of the Message Service, we will create
A fake version that returns the same
Message every time.
This makes sure we get a result
From the click method, and also
That the message service will never fail. ***
96. @jeffreymckenzie
------------- BEGIN [FAS_Tests].[Setup] -------------------
CREATE PROC FAS_Tests.Setup AS
BEGIN
--create fakes for tests against tables
EXEC tSQLt.FakeTable 'dbo.OrderDetail'
EXEC tSQLt.FakeTable 'dbo.Order'
EXEC tSQLt.FakeTable 'dbo.Customer'
-- populate fake tables
EXEC FAS_Tests.InsertTestData
END
GO
------------- END [FAS_Tests].[Setup] ---------------------
In the next part of the install script,
We are going to create the setup procedure.
If you have a procedure called Setup
In your test class, or schema, tSQLt
Will run that proc before every test.
We can use that to our advantage
And do some setup work
that every test will need.
First thing we do is create some Fakes….
97. @jeffreymckenzie
------------- BEGIN [FAS_Tests].[Setup] -------------------
CREATE PROC FAS_Tests.Setup AS
BEGIN
--create fakes for tests against tables
EXEC tSQLt.FakeTable 'dbo.OrderDetail'
EXEC tSQLt.FakeTable 'dbo.Order'
EXEC tSQLt.FakeTable 'dbo.Customer'
-- populate fake tables
EXEC FAS_Tests.InsertTestData
END
GO
------------- END [FAS_Tests].[Setup] ---------------------
For the tables we want to test.
The fake table command in tSQLt replaces
the actual table with an empty copy
That has no constraints.
That way it's isolated from the rest of the database
And we can test operations on that table alone.
After we create the fakes, we can use the proc
We created before to insert test data
about Ronald and Ron ***
100. @jeffreymckenzie
And finally the Order table,
Which matches the customer
to the particular order.
Note that this only happens
when a test is run – before or after a test
These tables have whatever data
Was in them originally. ***
101. @jeffreymckenzie
----------- BEGIN GivenMerge-ThenCustomerIsCorrect --------------
CREATE PROC FAS_Tests.[test GivenMerge-ThenCustomersIsCorrect]
AS
BEGIN
--Arrange
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 --CustomerId we want to keep
DECLARE @ExpectedRonFirstName NVARCHAR(50) =
Now we're ready to create our first test.
tSQLt requires that every test be a procedure
That starts with the word TEST.
What we want to accomplish in this test
Is to do the merge, then ensure that data
In the Customers Table is correct.
So I've started the arrange section here.
Remember that when this test starts,
All of the tables will be populated… ***
102. @jeffreymckenzie
----------- BEGIN GivenMerge-ThenCustomerIsCorrect --------------
CREATE PROC FAS_Tests.[test GivenMerge-ThenCustomerIsCorrect]
AS
BEGIN
--Arrange
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 --CustomerId we want to keep
DECLARE @ExpectedRonFirstName NVARCHAR(50) =
..because the setup has already run.
So to set the ExpectedRonFirstName variable,
What are a couple of ways we could do that?
***
103. @jeffreymckenzie
----------- BEGIN GivenMerge-ThenCustomerIsCorrect --------------
CREATE PROC FAS_Tests.[test GivenMerge-ThenCustomerIsCorrect]
AS
BEGIN
--Arrange
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 --CustomerId we want to keep
DECLARE @ExpectedRonFirstName NVARCHAR(50) =
So to set the ExpectedRonFirstName variable,
What are a couple of ways we could do that?
Hardcode name –
Select based on ID --
***
104. @jeffreymckenzie
----------- BEGIN GivenMerge-ThenCustomerIsCorrect --------------
CREATE PROC FAS_Tests.[test GivenMerge-ThenCustomerIsCorrect]
AS
BEGIN
--Arrange
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 --CustomerId we want to keep
DECLARE @ExpectedRonFirstName NVARCHAR(50) = (
SELECT FirstName FROM dbo.Customer
WHERE CustomerId = @RonId
)
If we select the value based on the ID,
That allows us to test other IDs without
Having to change a hardcoded string.
***
105. @jeffreymckenzie
----------- BEGIN GivenMerge-ThenCustomerIsCorrect --------------
CREATE PROC FAS_Tests.[test GivenMerge-ThenCustomerIsCorrect]
AS
BEGIN
--Arrange
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 --CustomerId we want to keep
DECLARE @ExpectedRonFirstName NVARCHAR(50) = (
SELECT FirstName FROM dbo.Customer
WHERE CustomerId = @RonId
)
If we select the value based on the ID,
That allows us to test other IDs without
Having to change a hardcoded string.
***
106. @jeffreymckenzie
DECLARE @ExpectedRonFirstName NVARCHAR(50) = (
SELECT FirstName FROM dbo.Customer
WHERE CustomerId = @RonId
)
DECLARE @ExpectedRonLastName NVARCHAR(50)= (
SELECT LastName FROM dbo.Customer
WHERE CustomerId = @RonId
)
DECLARE @ExpectedLoyaltyId INT = (
SELECT LoyaltyId FROM dbo.Customer
WHERE CustomerId = @RonId
)
Now we can do the same for all of the other
Values in the Customer table.
Now, how do we do the actual comparison
Between the expected values and the actual values?
We can do so through a table compare…
***
107. @jeffreymckenzie
CREATE TABLE FAS_Tests.Expected(
CustomerId INT NOT NULL,
FirstName NVARCHAR(50) NOT NULL,
LastName NVARCHAR(50) NOT NULL,
LoyaltyId INT NOT NULL
)
-only Ron should exist
INSERT INTO FAS_Tests.Expected(
CustomerId, FirstName, LastName, LoyaltyId)
VALUES(@RonId, @ExpectedRonFirstName,
, @ExpectedRonLastName
, @ExpectedLoyaltyId
)
First we create an Expected table
Inside our FAS_Tests schema, again,
So it's separated from the application code,
And then we insert the expected values
Into that table, because we should only see
one record when the merge is complete.
That's it for the arrange section – everything
Is set up to see if our test is correct
Once the test is executed. Let's finish the test: ***
108. @jeffreymckenzie
--Act
EXEC dbo.MergeCustomer
@DuplicateCustomer = @RonaldId,
@CustomerToKeep = @RonId
--Assert
EXEC tSQLt.AssertEqualsTable
@Expected = N'FAS_Tests.Expected',
@Actual = N'dbo.Customer'
@FailMsg = N'Customer table has incorrect data.'
END
GO
-------- END GivenMergeCustomers-ThenCustomerIsCorrect ------------
For the Act section, we will execute
The MergeCustomer stored procedure.
For our assert section, we are going to use
The tSQLt AssertEqualsTable assertion –
This determines whether or not two tables
Have the same data – we are comparing
The expected table in our test schema
With the actual Customer table,
Which in this case is our Fake table.
109. @jeffreymckenzie
1. Create Test Install Script
2. Create Test Run Script
So we will run our Test Install Script
to create our test class with the latest tests.
In order to run this test,
We will need to start
on our test run script.
It's pretty simple.
***
110. @jeffreymckenzie
USE Food_And_Stuff
/*
EXEC tSQLt.RunAll
GO
EXEC tSQLt.Run
N'FAS_Tests'
GO */
EXEC tSQLt.Run
N'FAS_Tests.[test GivenMergeCustomers-ThenCustomerIsCorrect]'
GO
There are three ways to run a test in tSQLt –
First is the RunAll command, which runs all tests
In all test classes.
The second is the Run command with a class name,
Which will run all tests in that test class.
Finally you can call the Run command with
A single test name, which will run only that class.
[ASK]
So if we run our test, what's going to happen? ***
111. @jeffreymckenzie
(0 row(s) affected)
[FAS_Tests].[test GivenMergeCustomers...] failed: (Error) Could not
find stored procedure 'dbo.MergeCustomer'.[16,62]{MergeCustomer,49}
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Dur(ms)|Result|
+--+--------------------------------+-------+------+
|1 |[FAS_Tests].[test GivenMerge...]| 236|Error |
-------------------------------------------------------------------
Msg 50000, Level 16, State 10, Line 1: Test Case Summary: 1 test
case(s) executed, 0 succeeded, 0 failed, 1 errored.
-------------------------------------------------------------------
Here's the output – tSQLt shows us
The actual error, that the MergeCustomer
Stored proc can't be found,
Then shows us the test summary, including
The name of the test, the duration,
And the result. Because it's red, we know
Immediately that it failed.
112. @jeffreymckenzie
So if this is our Customer table,
And we want to eliminate the Ronald record,
What's the least amount of effort we can do
To get this test to pass?
***
114. @jeffreymckenzie
USE Food_And_Stuff
GO
CREATE PROCEDURE [dbo].[MergeCustomer]
@DuplicateCustomer INT,
@CustomerToKeep INT
AS
BEGIN
DELETE dbo.Customer
WHERE CustomerId = @DuplicateCustomer
END
GO
Yep, that's right – let's
Run our test again….
115. @jeffreymckenzie
(1 row(s) affected)
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Dur(ms)|Result |
+--+--------------------------------+-------+-------+
|1 |[FAS_Tests].[test GivenMerge...]| 54|Success|
-------------------------------------------------------------------
Test Case Summary: 1 test case(s) executed, 1 succeeded, 0 failed,
0 errored.
-------------------------------------------------------------------
Now our test is passing.
Let's write our next test,
This time for the order table.
116. @jeffreymckenzie
This is a little different –
Remember we want to keep all
of the order history, so what should we
Expect the order table at the bottom
To look like once the merge is complete?
[CustomerId should be two for both orders]
Right, so let's write a test for that -- ***
117. @jeffreymckenzie
--------- BEGIN GivenMergeCustomers-ThenOrderIsCorrect ------------
CREATE PROC FAS_Tests.[test GivenMergeCustomers-ThenOrderIsCorrect]
AS
BEGIN
--Assert
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 --Customer Id we want to keep
CREATE TABLE FAS_Tests.Expected(
OrderId INT NOT NULL,
CustomerId INT NOT NULL
)
We will need to write the test a little differently
For this one – for the customer test,
We only needed one record, so we could
Insert one record into the expected table.
Here, we could have any number of orders
Between the two customers –
So how should we fill the expected table here? ***
118. @jeffreymckenzie
CREATE TABLE FAS_Tests.Expected(
OrderId INT NOT NULL,
CustomerId INT NOT NULL
)
--only orders for Ron (CustomerId 2) should exist
INSERT INTO FAS_Tests.Expected(
OrderId,
CustomerId
)
SELECT OrderId, @RonId
FROM dbo.[Order]
WHERE CustomerId = @RonaldId
OR CustomerId = @RonId
Essentially what we are doing here
Is selecting every record in the order table
For both customers, and inserting
The order id, as well as the customerId
We want to keep.
Let's finish with the act and assert
***
119. @jeffreymckenzie
--Act
EXEC dbo.MergeCustomer
@DuplicateCustomer = @RonaldId,
@CustomerToKeep = @RonId
--Assert
EXEC tSQLt.AssertEqualsTable
@Expected = N'FAS_Tests.Expected',
@Actual = N'dbo.Order'
@FailMsg = N'Order table has incorrect data.'
END
GO
-------- END GivenMergeCustomers-ThenOrderIsCorrect ------------
This is the same act and arrange as the last test,
Except we are now checking the Order table.
Let’s add our new test to the script… ***
121. @jeffreymckenzie
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Dur(ms)|Result |
+--+----------------------------------+-------+-------+
|1 |[FAS_Tests].[test Given...Customer| 37|Success|
|2 |[FAS_Tests].[test Given...Order | 33|Failure|
-------------------------------------------------------------------
Msg 50000, Level 16, State 10, Line 7, Test Case Summary:
2 test case(s) executed, 1 succeeded, 1 failed, 0 errored.
-------------------------------------------------------------------
So our customer test is passing,
But our order test is not – which is good
Because we haven’t written the code yet.
When we are using the AssertEqualsTable
Assertion, we get an additional error message… ***
122. @jeffreymckenzie
(1 row(s) affected)
[FAS_Tests].[test GivenMergeCustomers-ThenOrderIsCorrect] failed:
(Failure) Order table has incorrect data.
|_m_|OrderId|CustomerId|
+---+-------+----------+
|< |11 |2 |
|= |22 |2 |
|> |11 |1 |
This graphical table shows us
Exactly how the data is incorrect
A less than sign means that the record
Is in the expected table but not the actual table –
The greater than sign means the record
Is in the actual table but not the expected table,
And the equals sign means it’s in both tables.
This output can help you troubleshoot data issues. ***
123. @jeffreymckenzie
So now we want to make our order table
Have all the orders for Ronald and Ron
Belong to Ron – what’s the least amount
Of code we need to accomplish this?
[UPDATE CustomerId column]
So let’s update our procedure…. ***
124. @jeffreymckenzie
CREATE PROCEDURE [dbo].[MergeCustomer]
@DuplicateCustomer INT,
@CustomerToKeep INT
AS
BEGIN
DELETE dbo.Customer
WHERE CustomerId = @DuplicateCustomer
UPDATE dbo.[Order]
SET CustomerId = @CustomerToKeep
WHERE CustomerId = @DuplicateCustomer
OR CustomerId = @CustomerToKeep
END
GO
We’ve added our update statement,
And we will run our tests again ***…
125. @jeffreymckenzie
(1 row(s) affected)
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Dur(ms)|Result |
+--+----------------------------------------------+-------+-------+
|1 |[FAS_Tests].[test GivenMerge...Customer] | 37|Success|
|2 |[FAS_Tests].[test GivenMerge...Order] | 40|Success|
-------------------------------------------------------------------
Test Case Summary: 2 test case(s) executed, 2 succeeded, 0 failed,
0 errored.
-------------------------------------------------------------------
And now both are passing. ***
126. @jeffreymckenzie
Now because OrderDetail is tied to Order,
OrderDetail will stay the same,
So we don’t need a test for that,
Because we are not performing any action
On that table.
Our tests are passing, so let’s try this
On the live table – here’s our script … ***
127. @jeffreymckenzie
USE Food_And_Stuff
GO
BEGIN TRAN
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 /* Customer Id we want to keep */
EXEC FAS_Tests.InsertTestData
EXEC dbo.MergeCustomer
@DuplicateCustomer = @RonaldId,
@CustomerToKeep = @RonId
SELECT * FROM dbo.Customer
SELECT * FROM dbo.[Order]
SELECT * FROM dbo.OrderDetail
ROLLBACK TRAN
GO
We are going to run this in a transaction
So we can reset the tables and run it repeatedly.
First we have our regular customer IDs for
Ronald and Ron.
Then we run our insert data procedure,
Execute the merge procedure,
Then do some selects to see
What the data looks like….
***
128. @jeffreymckenzie
Msg 547, Level 16, State 0
Procedure MergeCustomer, Line 7 [Batch Start Line 2]
The DELETE statement conflicted with the REFERENCE
constraint "FK_Order_Customer_CustomerId".
The conflict occurred in database "Food_And_Stuff",
table "dbo.Order", column 'CustomerId’.
The statement has been terminated.
And we have an error –
What’s going on here?
[DELETED a customer record
still attached to an Order.]
Remember I said when you create
A fake table in tSQLt it creates
A blank copy without restraints?
We didn’t get this error in our test
Because we had no constraints. ***
129. @jeffreymckenzie
------------- BEGIN [FAS_Tests].[Setup] -------------------
CREATE PROC FAS_Tests.Setup AS
BEGIN
--create fakes for tests against tables
EXEC tSQLt.FakeTable 'dbo.OrderDetail'
EXEC tSQLt.FakeTable 'dbo.Order'
EXEC tSQLt.FakeTable 'dbo.Customer’
EXEC tSQLt.ApplyConstraint N'dbo.Order',
N'FK_Order_Customer_CustomerId'
-- populate fake tables
EXEC FAS_Tests.InsertTestData
END
GO
------------- END [FAS_Tests].[Setup] ---------------------
Fortunately we have a way
To add constraints to our tests,
Using the tSQLt.ApplyConstraint command.
After we create our fake table, all
Constraints are removed, but we can then
Apply individual constraints using the
Table name and constraint name – here
We are applying the foreign key constraint
Between the Order and Customer tables. ***
130. @jeffreymckenzie
[FAS_Tests].[test GivenMerge...CustomerIsCorrect] failed: (Error)
DELETE statement conflicted with "FK_Order_Customer_CustomerId".
The conflict occurred in database "Food_And_Stuff", table
"dbo.Order", column 'CustomerId'.[16,0]{MergeCustomer,7}
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Dur(ms)|Result|
+--+--------------------------------+-------+------+
|1 |[FAS_Tests].[test GivenMerge...]| 236|Error |
-------------------------------------------------------------------
Msg 50000, Level 16, State 10, Line 1: Test Case Summary: 1 test
case(s) executed, 0 succeeded, 0 failed, 1 errored.
-------------------------------------------------------------------
When we run the test again,
We get the same error we did when running live.
So we now have a failing test that we need to fix.
Let’s take a look at our
MergeCustomer procedure… ***
131. @jeffreymckenzie
CREATE PROCEDURE [dbo].[MergeCustomer]
@DuplicateCustomer INT,
@CustomerToKeep INT
AS
BEGIN
DELETE dbo.Customer
WHERE CustomerId = @DuplicateCustomer
UPDATE dbo.[Order]
SET CustomerId = @CustomerToKeep
WHERE CustomerId = @DuplicateCustomer
OR CustomerId = @CustomerToKeep
END
GO
Again , what’s the simplest change we can make
To get our test to pass?
[flip the statements…] ***
132. @jeffreymckenzie
CREATE PROCEDURE [dbo].[MergeCustomer]
@DuplicateCustomer INT,
@CustomerToKeep INT
AS
BEGIN
UPDATE dbo.[Order]
SET CustomerId = @CustomerToKeep
WHERE CustomerId = @DuplicateCustomer
OR CustomerId = @CustomerToKeep
DELETE dbo.Customer
WHERE CustomerId = @DuplicateCustomer
END
GO
Right, we make the update statement first,
So when we do the customer delete,
That customer will have no relationship
With the Order table.
Let’s run our tests again…. ***
133. @jeffreymckenzie
(1 row(s) affected)
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Dur(ms)|Result |
+--+----------------------------------------------+-------+-------+
|1 |[FAS_Tests].[test GivenMerge...Customer] | 37|Success|
|2 |[FAS_Tests].[test GivenMerge...Order] | 40|Success|
-------------------------------------------------------------------
Test Case Summary: 2 test case(s) executed, 2 succeeded, 0 failed,
0 errored.
-------------------------------------------------------------------
And now we are back to passing tests.
***
134. @jeffreymckenzie
USE Food_And_Stuff
GO
BEGIN TRAN
DECLARE @RonaldId INT= 1
DECLARE @RonId INT= 2 /* Customer Id we want to keep */
EXEC FAS_Tests.InsertTestData
EXEC dbo.MergeCustomer
@DuplicateCustomer = @RonaldId,
@CustomerToKeep = @RonId
SELECT * FROM dbo.Customer
SELECT * FROM dbo.[Order]
SELECT * FROM dbo.OrderDetail
ROLLBACK TRAN
GO
Let’s try our live test again and see
What that returns….
Again we are inserting test data
And performing the merge
against the actual table….
***
135. @jeffreymckenzie
Here’s the output – no errors
And it has what we expect,
Just the one record for Ron,
Who is attached to all of the orders now.
As we continue to add features
To this, we now have a set of tests
We can use to make sure everything
Is working properly.
***
136. @jeffreymckenzie
So congratulations everybody –
Ron is very happy with our work,
And has offered everyone
A five dollar gift card to Food and Stuff.
=========
https://vignette.wikia.nocookie.net/parksandrecreation/images/0/06/Foo
d_and_Stuff_2.png/revision/latest?cb=20120730155117
http://parksandrecreation.wikia.com/wiki/File:Food_and_Stuff_2.png
137. @jeffreymckenzie
That’s all I have….
=========
http://parksandrecreation.wikia.com/wiki/Food_and_Stuff
https://vignette.wikia.nocookie.net/parksandrecreation/images/1/15/Foo
d_and_Stuff.png/revision/latest?cb=20120730155051
138. Hey, You Got
Your TDD
In My SQL DB!
Jeff McKenzie
@jeffreymckenzie
mail@mcknz.com
So thank you for listening & for being here.
So does anybody have any questions about --
TDD, the tSQLt Framework, or testing in general?