forked from ericgu/RecipeManagerKata
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Port_Adapter_Simulator_Kata.html
113 lines (102 loc) · 6.26 KB
/
Port_Adapter_Simulator_Kata.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Port/Adapter/Simulator Kata</title>
</head>
<body>
<h1>Introduction</h1>
<p>This kata is practice for understanding and applying the concepts of ports, adapters, and simulators to a small program. If you are new to these concepts, I suggest reading
the background section and the linked references before you start.</p>
<h1>Directions</h1>
<p>Your goal is to modify the starting program - a simple windows forms editor - so that the methods in it are unit testable without directly using the file system. There are
likely other modifications that you may want to make as well. </p>
<p>Approach it in whatever order you would like. If you'd like some guidance, see the next section.</p>
<h2>Suggested approach</h2>
<p>There are a lot of different orders here that work; I've chosen one that I like. The important concerns are to get a good adapter definition, and try to do as much of the
transformation through refactoring (resharper if you have it) as possible. </p>
<il>
<ol>
<li>Refactor the file system code into a separate class so that it
is contained.</li>
<li>Create the adapter abstraction on top of it. This is just an
interface that the new file system class expresses.</li>
<li>Figure out what the adapter abstraction should be. Is it
convenient for the application to use? Is it at the right level of
abstraction? </li>
<li>Use refactoring to morph the existing abstraction into the one
that you want to have. Repeat for each change that you want to make.
</li>
<li>Using TDD, create a simulator for the port that you can use for your unit tests.</li>
<li>Retarget the simulator tests that you just created and run them
against the file system adapter. If there are differences in
behavior, decide which is correct (simulator or file system
adapter), and modify appropriately.</li>
<li>Bring the object editor code under test by writing tests for each of the methods that deal with the file system</li>
</ol>
</il>
<p>At this point, you have the file system operations abstracted and
contained. Use the same approach to deal with the UI.</p>
<h1>Background on ports, adapters, and simulators</h1>
<p>This section is a summary of a pretty broad topic, and I'm going to
simplify and generalize to keep it simple.</p>
<p>The concept of "port" comes from <a href="http://alistair.cockburn.us/Hexagonal+architecture">Cockburn's
Hexagonal Architecture</a>.. Simply speaking, a port is a connection that a
program has to something external, something like a database or a user
interface. The sample application has two ports - the user interface of the
application (which displays data and dispatches user events), and the file
system, which can store and retrieve the editor information.</p>
<p>Based on a program's usage of a port, we can derive an abstraction that
describes how the program uses the port, and then write a concrete adapter
that implements that abstraction. Very simply, we could do something like:</p>
<pre>class FileSystemAdapter: IFileSystem {...}</pre>
<p>and it will work fine. However, the real power is in the "adapt" part of
the adapter; rather than maintaining the abstraction of the underlying
service, the abstraction should be expressed using application-level
concepts. The adapter should present an interface that is exactly what the
program would like it to be. To take a simple example from the UI side, a UI
(aka "port")-level concept might be the user pressing a button, while the
application concept might be "SaveRequested". </p>
<p>Another way to think about it is that the purpose of the adapter is to
encapsulate the real-world details that the program doesn't really want to
care about.</p>
<p>This is a fairly typical approach to getting a program under test, and,
at this point, we can write mocks and merrily go on with our testing. But
there are problems with this approach:</p>
<ol>
<li>Mock code tends to be duplicated in multiple places in the codebase.</li>
<li>Mock code - even mock code with some complexity - is rarely tested.</li>
<li>Mocks created with mocking libraries require the developer to learn
a new language - the mocking language - and do not generally participate
in refactoring the way program code does. </li>
<li>There is no verification that the behavior of the mock code is a
faithful simulation of the real code. </li>
</ol>
<p>The way to avoid this issues is a special kind of adapter known as a
simulator, which I learned about from<a href="http://arlobelshee.com/mock-free-example-part-2-simulators/">
Arlo Belshee</a>. A simulator is an implementation of an adapter that is
designed for unit testing. In our example, it might be a FileSystemSimulator
which implements the adapter interface but stores files in memory. </p>
<p>The simulator is created using TDD, so we know that it works the way that
we expect. But the unit tests created are not specific to the simulator,
they are specific to the adapter interface. That means we can take the unit
tests, run them against all of the adapters (simulators and real adapters),
and verify that all the systems have the same behavior. We can then know
that the tests that we write using the simulator are using a faithful
simulation of the "real adapter". </p>
<h1>Evaluation Thoughts</h1>
<p>This section is definitely full of spoilers, so it's presented in
white-on-white text (highlight it to read) Read this after you have finished
the kata.</p>
<font color="white">
<ol>
<li>At what level of abstraction is the storage adapter expressed? I
think something like "ObjectStorage" is the right level; what the
application cares is that storage is possible and works in a specific
way; where the objects are stored is an implementation detail. </li>
<li>How happy are you with the resulting code that you ended up? Does it
seem to be well-factored, etc.?</li>
</ol>
</font>
</body>
</html>