testing web apis
DESCRIPTION
Jakob Mattsson «Testing web APIs» Frontend Dev Conf'14 www.fdconf.byTRANSCRIPT
![Page 1: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/1.jpg)
jakobm.com
@jakobmattsson
I’m a coder first and foremost. I also help companies recruit coders, train coders and architect software. Sometimes I do technical due diligence and speak at conferences. !
Want more? Read my story or blog.
![Page 2: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/2.jpg)
![Page 3: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/3.jpg)
![Page 4: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/4.jpg)
Social coding
![Page 5: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/5.jpg)
FishBrain is the fastest, easiest way to log and share your fishing.!Get instant updates from anglers on nearby lakes, rivers, and the coast.
![Page 6: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/6.jpg)
Testing web APIsSimple and readable
![Page 7: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/7.jpg)
Example - Testing a blog API
• Create a blog • Create two blog entries • Create two users • Create a lotal of three comments from
those users, on the two posts • Request the stats for the blog and
check if the given number of entries and comments are correct
![Page 8: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/8.jpg)
post('/blogs', { name: 'My blog' }, function(err, blog) { ! post(concatUrl('blogs', blog.id, 'entries'), { title: 'my first post’, body: 'Here is the text of my first post' }, function(err, entry1) { ! post(concatUrl('blogs', blog.id, 'entries'), { title: 'my second post’, body: 'I do not know what to write any more...' }, function(err, entry2) { ! post('/user', { name: 'john doe’ }, function(err, visitor1) { ! post('/user', { name: 'jane doe' }, function(err, visitor2) { ! post('/comments', { userId: visitor1.id, entryId: entry1.id, text: "well written dude" }, function(err, comment1) { ! post('/comments', { userId: visitor2.id, entryId: entry1.id, text: "like it!" }, function(err, comment2) { ! post('/comments', { userId: visitor2.id, entryId: entry2.id, text: "nah, crap" }, function(err, comment3) { ! get(concatUrl('blogs', blog.id), function(err, blogInfo) { assertEquals(blogInfo, { name: 'My blog', numberOfEntries: 2, numberOfComments: 3 }); }); }); }); }); }); }); }); }); });
![Page 9: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/9.jpg)
*No, this is not a tutorial
Promises 101*
![Page 10: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/10.jpg)
getJSON({ url: '/somewhere/over/the/rainbow', success: function(result) { // deal with it } });
![Page 11: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/11.jpg)
getJSON({ url: '/somewhere/over/the/rainbow', success: function(result) { // deal with it } });
rainbows.then(function(result) { // deal with it });
var rainbows = getJSON({ url: '/somewhere/over/the/rainbow' });
![Page 12: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/12.jpg)
getJSON({ url: '/somewhere/over/the/rainbow', success: function(result) { // deal with it } });
rainbows.then(function(result) { // deal with it });
var rainbows = getJSON({ url: '/somewhere/over/the/rainbow' });f(rainbows);
![Page 13: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/13.jpg)
Why is that a good idea?
• Loosen up the coupling • Superior error handling • Simplified async
![Page 14: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/14.jpg)
Why is that a good idea?
• Loosen up the coupling • Superior error handling • Simplified async
But in particular, it abstracts away the temporal dependencies in your program (or in this case, test)
![Page 15: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/15.jpg)
That’s enough 101(even though people barely talk about
the last - and most important - idea)
![Page 16: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/16.jpg)
Promises are not new
![Page 17: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/17.jpg)
Promises are not newhttp://gi
thub.com/kriskowal
/q
http://www.html5rocks.com/en/t
utorials/es6/promises
http://domenic.me/2012/10/14/youre-missing-the-point-of-promises
http://www.promisejs.org
https://github.com/bellbind/using-promise-q
https://github.com
/tildeio/rsvp.js
https://github.com/cujojs/when
![Page 18: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/18.jpg)
They’ve even made it into ES6
![Page 19: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/19.jpg)
They’ve even made it into ES6
Already implemented natively in
Firefox 30Chrome 33
![Page 20: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/20.jpg)
So why are you not using them?
![Page 21: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/21.jpg)
So why are you not using them?
![Page 22: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/22.jpg)
How to draw an owl
![Page 23: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/23.jpg)
1. Draw some circles
How to draw an owl
![Page 24: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/24.jpg)
1. Draw some circles
2. Draw the rest of the owl
How to draw an owl
![Page 25: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/25.jpg)
We want to draw owls.
!
Not circles.
![Page 26: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/26.jpg)
getJSON('story.json').then(function(story) { addHtmlToPage(story.heading); ! // Map our array of chapter urls to // an array of chapter json promises. // This makes sture they all download parallel. return story.chapterUrls.map(getJSON) .reduce(function(sequence, chapterPromise) { // Use reduce to chain the promises together, // adding content to the page for each chapter return sequence.then(function() { // Wait for everything in the sequence so far, // then wait for this chapter to arrive. return chapterPromise; }).then(function(chapter) { addHtmlToPage(chapter.html); }); }, Promise.resolve()); }).then(function() { addTextToPage('All done'); }).catch(function(err) { // catch any error that happened along the way addTextToPage("Argh, broken: " + err.message); }).then(function() { document.querySelector('.spinner').style.display = 'none'; });
As announced for ES6
![Page 27: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/27.jpg)
That’s circles. !
Nice circles! !
Still circles.
![Page 28: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/28.jpg)
browser.init({browserName:'chrome'}, function() { browser.get("http://admc.io/wd/test-pages/guinea-pig.html", function() { browser.title(function(err, title) { title.should.include('WD'); browser.elementById('i am a link', function(err, el) { browser.clickElement(el, function() { browser.eval("window.location.href", function(err, href) { href.should.include('guinea-pig2'); browser.quit(); }); }); }); }); }); });
Node.js WebDriver Before promises
![Page 29: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/29.jpg)
browser .init({ browserName: 'chrome' }) .then(function() { return browser.get("http://admc.io/wd/test-pages/guinea-pig.html"); }) .then(function() { return browser.title(); }) .then(function(title) { title.should.include('WD'); return browser.elementById('i am a link'); }) .then(function(el) { return browser.clickElement(el); }) .then(function() { return browser.eval("window.location.href"); }) .then(function(href) { href.should.include('guinea-pig2'); }) .fin(function() { return browser.quit(); }) .done();
After promises
![Page 30: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/30.jpg)
Used to be callback-hell.
!
Now it is then-hell.
![Page 31: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/31.jpg)
![Page 32: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/32.jpg)
![Page 33: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/33.jpg)
These examples are just a different way
of doing async. !
It’s still uncomfortable. It’s still circles!
![Page 34: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/34.jpg)
The point of promises:
![Page 35: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/35.jpg)
!
Make async code as straightforward
as sync code
The point of promises:
![Page 36: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/36.jpg)
1 Promises out: Always return promises - not callback
2 Promises in: Functions should accept promises as well as regular values
3 Promises between: Augment promises as you augment regular objects
Three requirements
![Page 37: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/37.jpg)
Let me elaborate
![Page 38: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/38.jpg)
Example - Testing a blog API
• Create a blog • Create two blog entries • Create some users • Create some comments from those
users, on the two posts • Request the stats for the blog and
check if the given number of entries and comments are correct
![Page 39: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/39.jpg)
post('/blogs', { name: 'My blog' }, function(err, blog) { ! post(concatUrl('blogs', blog.id, 'entries'), { title: 'my first post’, body: 'Here is the text of my first post' }, function(err, entry1) { ! post(concatUrl('blogs', blog.id, 'entries'), { title: 'my second post’, body: 'I do not know what to write any more...' }, function(err, entry2) { ! post('/user', { name: 'john doe’ }, function(err, visitor1) { ! post('/user', { name: 'jane doe' }, function(err, visitor2) { ! post('/comments', { userId: visitor1.id, entryId: entry1.id, text: "well written dude" }, function(err, comment1) { ! post('/comments', { userId: visitor2.id, entryId: entry1.id, text: "like it!" }, function(err, comment2) { ! post('/comments', { userId: visitor2.id, entryId: entry2.id, text: "nah, crap" }, function(err, comment3) { ! get(concatUrl('blogs', blog.id), function(err, blogInfo) { assertEquals(blogInfo, { name: 'My blog', numberOfEntries: 2, numberOfComments: 3 }); }); }); }); }); }); }); }); }); });
https:// github.com/
jakobmattsson/ z-presentation/
blob/master/ promises-in-out/
1-naive.js
1
Note: without narration, this slide lacks a lot of context. Open the file above and read the
commented version for the full story.
![Page 40: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/40.jpg)
post('/blogs', { name: 'My blog' }, function(err, blog) { ! var entryData = [{ title: 'my first post', body: 'Here is the text of my first post' }, { title: 'my second post', body: 'I do not know what to write any more...' }] ! async.forEach(entryData, function(entry, callback), { post(concatUrl('blogs', blog.id, 'entries'), entry, callback); }, function(err, entries) { ! var usernames = ['john doe', 'jane doe']; ! async.forEach(usernames, function(user, callback) { post('/user', { name: user }, callback); }, function(err, visitors) { ! var commentsData = [{ userId: visitor[0].id, entryId: entries[0].id, text: "well written dude" }, { userId: visitor[1].id, entryId: entries[0].id, text: "like it!" }, { userId: visitor[1].id, entryId: entries[1].id, text: "nah, crap" }]; ! async.forEach(commentsData, function(comment, callback) { post('/comments', comment, callback); }, function(err, comments) { ! get(concatUrl('blogs', blog.id), function(err, blogInfo) { ! assertEquals(blogInfo, { name: 'My blog', numberOfEntries: 2, numberOfComments: 3 }); }); }); }); }); });
https:// github.com/
jakobmattsson/ z-presentation/
blob/master/ promises-in-out/
2-async.js
2
Note: without narration, this slide lacks a lot of context. Open the file above and read the
commented version for the full story.
![Page 41: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/41.jpg)
https:// github.com/
jakobmattsson/ z-presentation/
blob/master/ promises-in-out/
3-async-more-parallel.js
3
Note: without narration, this slide lacks a lot of context. Open the file above and read the
commented version for the full story.
post('/blogs', { name: 'My blog' }, function(err, blog) { ! async.parallel([ function(callback) { var entryData = [{ title: 'my first post', body: 'Here is the text of my first post' }, { title: 'my second post', body: 'I do not know what to write any more...' }]; async.forEach(entryData, function(entry, callback), { post(concatUrl('blogs', blog.id, 'entries'), entry, callback); }, callback); }, function(callback) { var usernames = ['john doe', 'jane doe’]; async.forEach(usernames, function(user, callback) { post('/user', { name: user }, callback); }, callback); } ], function(err, results) { ! var entries = results[0]; var visitors = results[1]; ! var commentsData = [{ userId: visitors[0].id, entryId: entries[0].id, text: "well written dude" }, { userId: visitors[1].id, entryId: entries[0].id, text: "like it!" }, { userId: visitors[1].id, entryId: entries[1].id, text: "nah, crap" }]; ! async.forEach(commentsData, function(comment, callback) { post('/comments', comment, callback); }, function(err, comments) { ! get(concatUrl('blogs', blog.id), function(err, blogInfo) { assertEquals(blogInfo, { name: 'My blog', numberOfEntries: 2, numberOfComments: 3 }); }); }); }); });
![Page 42: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/42.jpg)
post('/blogs', { name: 'My blog' }).then(function(blog) { ! var visitor1 = post('/user', { name: 'john doe' }); ! var visitor2 = post('/user', { name: 'jane doe' }); ! var entry1 = post(concatUrl('blogs', blog.id, 'entries'), { title: 'my first post', body: 'Here is the text of my first post' }); ! var entry2 = post(concatUrl('blogs', blog.id, 'entries'), { title: 'my second post', body: 'I do not know what to write any more...' }); ! var comment1 = all(entry1, visitor1).then(function(e1, v1) { post('/comments', { userId: v1.id, entryId: e1.id, text: "well written dude" }); }); ! var comment2 = all(entry1, visitor2).then(function(e1, v2) { post('/comments', { userId: v2.id, entryId: e1.id, text: "like it!" }); }); ! var comment3 = all(entry2, visitor2).then(function(e2, v2) { post('/comments', { userId: v2.id, entryId: e2.id, text: "nah, crap" }); }); ! all(comment1, comment2, comment3).then(function() { get(concatUrl('blogs', blog.id)).then(function(blogInfo) { assertEquals(blogInfo, { name: 'My blog', numberOfEntries: 2, numberOfComments: 3 }); }); }); });
https:// github.com/
jakobmattsson/ z-presentation/
blob/master/ promises-in-out/
4-promises-convoluted.js
4
Note: without narration, this slide lacks a lot of context. Open the file above and read the
commented version for the full story.
![Page 43: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/43.jpg)
var blog = post('/blogs', { name: 'My blog' }); !var entry1 = post(concatUrl('blogs', blog.get('id'), 'entries'), { title: 'my first post', body: 'Here is the text of my first post' }); !var entry2 = post(concatUrl('blogs', blog.get('id'), 'entries'), { title: 'my second post', body: 'I do not know what to write any more...' }); !var visitor1 = post('/user', { name: 'john doe' }); !var visitor2 = post('/user', { name: 'jane doe' }); !var comment1 = post('/comments', { userId: visitor1.get('id'), entryId: entry1.get('id'), text: "well written dude" }); !var comment2 = post('/comments', { userId: visitor2.get('id'), entryId: entry1.get('id'), text: "like it!" }); !var comment3 = post('/comments', { userId: visitor2.get('id'), entryId: entry2.get('id'), text: "nah, crap" }); !var allComments = [comment1, comment2, comment2]; !var blogInfoUrl = concatUrl('blogs', blog.get('id')); !var blogInfo = getAfter(blogInfoUrl, allComments); !assertEquals(blogInfo, { name: 'My blog', numberOfEntries: 2, numberOfComments: 3 });
https:// github.com/
jakobmattsson/ z-presentation/
blob/master/ promises-in-out/
5-promises-nice.js
5
Note: without narration, this slide lacks a lot of context. Open the file above and read the
commented version for the full story.
![Page 44: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/44.jpg)
![Page 45: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/45.jpg)
1 Promises out: Always return promises - not callback
2 Promises in: Functions should accept promises as well as regular values
Three requirements
![Page 46: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/46.jpg)
Awesome!
![Page 47: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/47.jpg)
1 Promises out: Always return promises - not callback
2 Promises in: Functions should accept promises as well as regular values
3 Promises between: Augment promises as you augment regular objects
Three requirements
![Page 48: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/48.jpg)
Augmenting objects is a sensitive topic
![Page 49: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/49.jpg)
Extending prototypes !
vs !
wrapping objects
![Page 50: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/50.jpg)
You’re writing Course of action
An app Do whatever you want
A library Do not modify thedamn prototypes
Complimentary decision matrix
![Page 51: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/51.jpg)
![Page 52: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/52.jpg)
Promises are already wrapped objects
![Page 53: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/53.jpg)
_(names) .chain() .unique() .shuffle() .first(3) .value()
Chaining usually requires a method to ”unwrap” or repeated wrapping
u = _(names).unique() s = _(u).shuffle() f = _(s).first(3)
![Page 54: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/54.jpg)
Promises already have a well-defined way of
unwrapping.
![Page 55: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/55.jpg)
_(names) .unique() .shuffle() .first(3) .then(function(values) { // do stuff with values... })
If underscore/lodash wrapped promises
![Page 56: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/56.jpg)
But they don’t
![Page 57: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/57.jpg)
Enter !
Z
![Page 58: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/58.jpg)
1 Deep resolution: Resolve any kind of object/array/promise/values/whatever
2 Make functions promise-friendly: Sync or async doesn’t matter; will accept promises
3 Augmentation for promises: jQuery/underscore/lodash-like extensions
What is Z?
![Page 59: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/59.jpg)
Deep resolution
var data = { userId: visitor1.get('id'), entryId: entry1.get('id'), text: 'well written dude' }; !Z(data).then(function(result) { ! // result is now: { // userId: 123, // entryId: 456, // text: 'well written dude' // } !});
Takes any object and resolves all promises in it.
!
Like Q and Q.all, but deep
1
![Page 60: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/60.jpg)
Promise-friendly functions
var post = function(url, data, callback) { // POSTs `data` to `url` and // then invokes `callback` }; !post = Z.bindAsync(post); !var comment1 = post('/comments', { userId: visitor1.get('id'), entryId: entry1.get('id'), text: 'well written dude' });
bindAsync creates a function that
takes promises as arguments and
returns a promise.
2
![Page 61: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/61.jpg)
Promise-friendly functions
var add = function(x, y) { return x + y; }; !add = Z.bindSync(add); !var sum = add(v1.get('id'), e1.get('id')); !var comment1 = post('/comments', { userId: visitor1.get('id'), entryId: entry1.get('id'), text: 'well written dude', likes: sum });
2
bindSync does that same, for functions that are not async
![Page 62: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/62.jpg)
Augmentation for promises
var commentData = get('/comments/42'); !var text = commentData.get('text'); !var lowerCased = text.then(function(text) { return text.toLowerCase(); });
3
Without augmentation
every operation has to be wrapped
in ”then”
![Page 63: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/63.jpg)
![Page 64: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/64.jpg)
Augmentation for promises
Z.mixin({ toLowerCase: function() { return this.value.toLowerCase(); } }); !var commentData = get('/comments/42'); !commentData.get('text').toLowerCase();
3
Z has mixin to solve this
!
Note that Z is not explicitly applied
to the promise
![Page 65: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/65.jpg)
Augmentation for promises
Z.mixin(zUnderscore); Z.mixin(zBuiltins); !var comment = get('/comments/42'); !comment.get('text').toLowerCase().first(5);
3
There are prepared packages to mixin
entire libraries
![Page 66: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/66.jpg)
1 Promises out: Always return promises - not callback
2 Promises in: Functions should accept promises as well as regular values
3 Promises between: Augment promises as you augment regular objects
Three requirements
![Page 67: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/67.jpg)
Make async code as straightforward
as sync code
Enough with the madness
![Page 68: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/68.jpg)
You don’t need a lib to do these things
![Page 69: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/69.jpg)
But please
![Page 70: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/70.jpg)
![Page 71: Testing web APIs](https://reader033.vdocuments.site/reader033/viewer/2022052822/554fb089b4c9057b298b50a2/html5/thumbnails/71.jpg)
www.jakobm.com @jakobmattsson !
github.com/jakobmattsson/z-core
Testing web APIsSimple and readable