-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathjavascript.atom.xml
More file actions
270 lines (178 loc) · 34.2 KB
/
Copy pathjavascript.atom.xml
File metadata and controls
270 lines (178 loc) · 34.2 KB
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title type="text">Alexis King's Blog: Posts tagged 'javascript'</title>
<link rel="self" href="http://lexi-lambda.github.io/feeds/javascript.atom.xml" />
<link href="http://lexi-lambda.github.io/tags/javascript.html" />
<id>urn:http-lexi-lambda-github-io:-tags-javascript-html</id>
<updated>2016-08-24T22:50:28Z</updated>
<entry>
<title type="text">Understanding the npm dependency model</title>
<link rel="alternate" href="http://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/?utm_source=javascript&utm_medium=Atom" />
<id>urn:http-lexi-lambda-github-io:-blog-2016-08-24-understanding-the-npm-dependency-model</id>
<published>2016-08-24T22:50:28Z</published>
<updated>2016-08-24T22:50:28Z</updated>
<author>
<name>Alexis King</name></author>
<content type="html">
<p>Currently, <a href="https://www.npmjs.com">npm</a> is <em>the</em> package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like <a href="https://bower.io">Bower</a> are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects <strong>you</strong> as a user or package developer.</p>
<!-- more-->
<h1 id="first-the-basics">First, the basics</h1>
<p>At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with <em>version ranges</em>. npm happens to use the <a href="http://semver.org">semver</a> versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages.</p>
<p>This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining <em>when</em> this is safe and when it’s not, and this is what I so frequently find that people get wrong.</p>
<h1 id="dependency-duplication-and-the-dependency-tree">Dependency duplication and the dependency tree</h1>
<p>Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a <em>tree</em> of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers.</p>
<p>For example, consider two packages, <code>foo</code> and <code>bar</code>. Each of them have their own set of dependencies, which can be represented as a tree:</p>
<pre><code>foo
├── hello ^0.1.2
└── world ^1.0.7
bar
├── hello ^0.2.8
└── goodbye ^3.4.0</code></pre>
<p>Imagine an application that depends on <em>both</em> <code>foo</code> and <code>bar</code>. Obviously, the <code>world</code> and <code>goodbye</code> dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of <code>hello</code>: both packages require conflicting versions.</p>
<p>Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, <strong>only one version of any particular package can be installed at a time</strong>. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously.</p>
<p>In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this:</p>
<pre><code>node_modules/
├── foo/
│ └── node_modules/
│ ├── hello/
│ └── world/
└── bar/
└── node_modules/
├── hello/
└── goodbye/</code></pre>
<p>Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own <code>node_modules</code> directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.)</p>
<p>This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like <code>ramda</code>, <code>lodash</code>, or <code>underscore</code>. If <code>foo</code> depends on <code>ramda@^0.19.0</code> but <code>bar</code> depends on <code>ramda@^0.22.0</code>, they can both coexist completely peacefully without any problems.</p>
<p>At first blush, this system is <em>obviously</em> better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks.</p>
<p>The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness.</p>
<p>The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication.</p>
<h1 id="dependency-isolation-and-values-that-pass-package-boundaries">Dependency isolation and values that pass package boundaries</h1>
<p>The earlier example of using <code>ramda</code> is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple.</p>
<p>Consider, for a moment, <code>react</code>. React components are very much <em>not</em> plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with <code>react@0.3.0</code> likely won’t work quite right with <code>react@15.3.1</code>.</p>
<p>With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this:</p>
<pre><code>awesome-button
└── react ^0.3.0
amazing-modal
└── react ^15.3.1</code></pre>
<p>Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error.</p>
<p>What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its <em>interface</em>, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself.</p>
<p>This might not sound <em>too</em> bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages <em>can</em> theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down.</p>
<p>Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by <a href="https://www.reddit.com/r/haskell/comments/4zc6y3/why_doesnt_cabal_use_a_model_like_that_of_npm/?ref=share&amp;ref_source=link">a Reddit thread discussing the npm model applied to Haskell</a>, and this flaw was touted as a reason why it couldn’t possibly work for such a static language.</p>
<p>Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like <code>instanceof</code>, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t <em>quite</em> the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies.</p>
<h2 id="peer-dependencies">Peer dependencies</h2>
<p>Normally, npm package dependencies are listed under a <code>"dependencies"</code> key in the package’s <code>package.json</code> file. There is, however, another, less-used key called <code>"peerDependencies"</code>, which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent.</p>
<p>This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency?</p>
<p>This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in <em>any place</em> in the package’s interface?</p>
<p>This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency.</p>
<p>To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of <code>ramda</code>:</p>
<div class="brush: js">
<div class="source">
<pre><span></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">merge</span><span class="p">,</span> <span class="nx">add</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;ramda&#39;</span>
<span class="kr">export</span> <span class="kr">const</span> <span class="nx">withDefaultConfig</span> <span class="o">=</span> <span class="p">(</span><span class="nx">config</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="nx">merge</span><span class="p">({</span> <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;.&#39;</span> <span class="p">},</span> <span class="nx">config</span><span class="p">)</span>
<span class="kr">export</span> <span class="kr">const</span> <span class="nx">add5</span> <span class="o">=</span> <span class="nx">add</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</pre></div>
</div>
<p>The first example here is pretty obvious: in <code>withDefaultConfig</code>, <code>merge</code> is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In <code>add5</code>, the example is a little trickier: the result of <code>add(5)</code> is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract <code>add5</code> has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so <code>ramda</code> can safely be a non-peer dependency.</p>
<p>Now let’s look at another example using the <code>jpeg</code> image library:</p>
<div class="brush: js">
<div class="source">
<pre><span></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">Jpeg</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;jpeg&#39;</span>
<span class="kr">export</span> <span class="kr">const</span> <span class="nx">createSquareBuffer</span> <span class="o">=</span> <span class="p">(</span><span class="nx">size</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="nx">createSquareJpeg</span><span class="p">(</span><span class="nx">size</span><span class="p">).</span><span class="nx">encode</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span>
<span class="kr">export</span> <span class="kr">const</span> <span class="nx">createSquareJpeg</span> <span class="o">=</span> <span class="p">(</span><span class="nx">size</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="k">new</span> <span class="nx">Jpeg</span><span class="p">(</span><span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="nx">size</span> <span class="o">*</span> <span class="nx">size</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
</pre></div>
</div>
<p>In this case, the <code>createSquareBuffer</code> function invokes a callback with an ordinary Node.js <code>Buffer</code> object, so the <code>jpeg</code> library is an implementation detail. If that were the only function exposed by this module, <code>jpeg</code> could safely be a non-peer dependency. However, the <code>createSquareJpeg</code> function violates that rule: it returns a <code>Jpeg</code> object, which is an opaque value with a structure defined exclusively by the <code>jpeg</code> library. Therefore, a package with the above module <em>must</em> list <code>jpeg</code> as a peer dependency.</p>
<p>This sort of restriction works in reverse, too. For example, consider the following module:</p>
<div class="brush: js">
<div class="source">
<pre><span></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">writeFile</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;fs&#39;</span>
<span class="kr">export</span> <span class="kr">const</span> <span class="nx">writeJpeg</span> <span class="o">=</span> <span class="p">(</span><span class="nx">filename</span><span class="p">,</span> <span class="nx">jpeg</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="nx">jpeg</span><span class="p">.</span><span class="nx">encode</span><span class="p">((</span><span class="nx">image</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span> <span class="nx">image</span><span class="p">,</span> <span class="nx">cb</span><span class="p">))</span>
</pre></div>
</div>
<p>The above module does not even <em>import</em> the <code>jpeg</code> package, yet it implicitly depends on the <code>encode</code> method of the <code>Jpeg</code> interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include <code>jpeg</code> as a peer dependency.</p>
<p>They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies.</p>
<h1 id="applying-the-npm-model-to-other-programming-languages">Applying the npm model to other programming languages</h1>
<p>The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating.</p>
<p>This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a <em>runtime</em> level. The reason npm is able to do what it does is because Node itself supports it.</p>
<p>That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help.</p>
<p>Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist.</p>
<p>From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it.</p></content></entry>
<entry>
<title type="text">Canonical factories for testing with factory_girl_api</title>
<link rel="alternate" href="http://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/?utm_source=javascript&utm_medium=Atom" />
<id>urn:http-lexi-lambda-github-io:-blog-2015-09-23-canonical-factories-for-testing-with-factory-girl-api</id>
<published>2015-09-23T16:30:12Z</published>
<updated>2015-09-23T16:30:12Z</updated>
<author>
<name>Alexis King</name></author>
<content type="html">
<p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won&rsquo;t catch the failure. I haven&rsquo;t found a very good solution to this problem aside from brittle, end-to-end integration tests.</p>
<p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p>
<!-- more-->
<h1 id="a-brief-overview-of-factorygirl">A brief overview of factory_girl</h1>
<p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails&rsquo; default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p>
<div class="brush: ruby">
<div class="source">
<pre><span></span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span> <span class="k">do</span>
<span class="n">factory</span> <span class="ss">:widget</span> <span class="k">do</span>
<span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="nb">id</span><span class="o">|</span> <span class="s1">&#39;Widget #&#39;</span> <span class="o">+</span> <span class="nb">id</span> <span class="p">}</span>
<span class="n">price</span> <span class="mi">10</span>
<span class="n">trait</span> <span class="ss">:expensive</span> <span class="k">do</span>
<span class="n">price</span> <span class="mi">1000</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
</div>
<p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p>
<div class="brush: ruby">
<div class="source">
<pre><span></span><span class="n">widget</span> <span class="o">=</span> <span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span> <span class="ss">:widget</span>
</pre></div>
</div>
<p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p>
<div class="brush: ruby">
<div class="source">
<pre><span></span><span class="n">expensive_widget</span> <span class="o">=</span> <span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span> <span class="ss">:widget</span><span class="p">,</span> <span class="ss">:expensive</span>
</pre></div>
</div>
<p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p>
<div class="brush: ruby">
<div class="source">
<pre><span></span><span class="n">fancy_widget</span> <span class="o">=</span> <span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span> <span class="ss">:widget</span><span class="p">,</span> <span class="ss">:expensive</span><span class="p">,</span> <span class="nb">name</span><span class="p">:</span> <span class="s1">&#39;Fancy Widget&#39;</span>
</pre></div>
</div>
<p>It works well, and it keeps initialization boilerplate out of individual tests.</p>
<h1 id="testing-on-the-front-end">Testing on the front-end</h1>
<p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p>
<div class="brush: js">
<div class="source">
<pre><span></span><span class="kd">var</span> <span class="nx">fancyWidget</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Widget</span><span class="p">({</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span>
<span class="nx">price</span><span class="o">:</span> <span class="mi">1000</span>
<span class="p">});</span>
</pre></div>
</div>
<p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it&rsquo;s possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p>
<h2 id="reusing-server-side-factories-with-factorygirlapi">Reusing server-side factories with factory_girl_api</h2>
<p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p>
<p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p>
<div class="brush: js">
<div class="source">
<pre><span></span><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span> <span class="s1">&#39;expensive&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Fancy Widget&#39;</span> <span class="p">});</span>
</pre></div>
</div>
<p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p>
<h2 id="the-problems-with-relying-on-the-server-for-data">The problems with relying on the server for data</h2>
<p>In my preliminary use of this tool, it works. In many ways, it&rsquo;s much nicer than duplicating logic in both places. However, I&rsquo;m not <em>completely</em> convinced it&rsquo;s the right solution yet.</p>
<p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn&rsquo;t really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p>
<p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p>
<h2 id="potential-improvements-and-other-paths-to-success">Potential improvements and other paths to success</h2>
<p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren&rsquo;t really pure unit tests, but also aren&rsquo;t fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn&rsquo;t work). I don&rsquo;t know.</p>
<p>Either way, I&rsquo;m interested in where this is headed, and I&rsquo;ll be curious to see if I run into any roadblocks using the workflow I&rsquo;ve created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I&rsquo;ve covered here. Take a look, and give them a spin!</p>
<ul>
<li><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></li>
<li><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></li></ul></content></entry></feed>