the two alexs

Koala 1.1rc3 released!

Hey everyone,

I’m happy to announce the release of Koala 1.1rc3!  Looking back, the previous release should have been a beta, since we’ve made a number of changes and improvements.

Batch API Interface Change

Most significantly, we’ve made a breaking change to the Batch API interface.  This was not an easy choice, but the original implementation wasn’t thread-safe (class variables and all that), an oversight that’s been resolved with the new batch interface (thanks to spiegela for his help).  It’s very similar, and we hope the change will not cause too much inconvenience.
it "can get two results at once" do
  # batch is now a method on GraphAPI instances,
  # yielding its own GraphBatchAPI object
  me, koppel = @api.batch do |batch_api|
    batch_api.get_object('me')
    batch_api.get_object('koppel')
  end
  me['id'].should_not be_nil
  koppel['id'].should_not be_nil
end
You can check out the updated examples at https://github.com/arsduo/koala/wiki/Batch-requests.

Other Changes

With 1.1rc3, we’ve added a few other improvements to Koala:
  • Added GraphAPI#put_video
  • NetHTTPService now supports global and per-call settings for ca_path, ca_file, and verify_mode (useful to those experiencing SSL problems)
  • We’ve switched from the JSON gem to MultiJson to make Koala compatible with Rubinius
Full documentation is available, as always, on the wiki; the full changelog since RC2 is here.

Installation

Just run [sudo|rvm] gem install koala –pre or add gem “koala”, “~> 1.1.0rc” to your Gemfile.

Final 1.1 Release

Please give 1.1rc3 a try and let us know if you find any issues!  If no big problems come up, we’ll release 1.1 in mid-July.

And don’t forget

To check out Facebook’s new Graph API Explorer, a neat new tool that lets you generate access tokens and play with the Graph API.  (Much like the OAuth Playground, but slicker and without the OAuth components.)


Best,

Alex and Chris
https://github.com/arsduo/koala

Koala 1.1rc released!

Hey everyone,
I’m excited to announce the release of the Koala 1.1 release candidate!
The big news here is support for Facebook’s batch API, which lets you bundle a number of operations into one request from Facebook:
me, friends = Koala::Facebook::GraphAPI.batch do
   @api.get_object('me')
   @api.get_connections('me', 'friends')
end
We’ve also added new convenience methods, improved others, and refactored the HTTP services to be more modular.  All changes are, of course, fully tested and non-breaking (though if you’re interacting directly with Koala’s HTTPServices, you should review the changes).
Documentation is available on the wiki, and the complete diff since 1.0 is available here.

Installation

Just run
[sudo] gem install koala --pre
or add
gem "koala", "~> 1.1.0rc"
to your Gemfile.

Changes

New methods:
  • You can now make batch requests to Facebook; we support the entire API, including file uploads, error handling, and FQL.
  • GraphAPI#get_comments_for_urls (pretty self-explanatory)
  • RestAPI#fql_multiquery, which simplifies both making the requests and using the results
Updated methods:
  • RealtimeUpdates now uses a GraphAPI object instead of its own API
  • RestAPI#rest_call now has an optional last argument for method, for calls requiring POST, DELETE, etc.
  • UploadableIO can take a filename (for the Ads API)
  • get_objects([]) returns [] instead of a Facebook error

Internal improvements:

  • HTTP services are more modular and can be changed on the fly
    • Includes support for uploading StringIOs and other non-files via Net::HTTP even when using TyphoeusService
  • Support for global proxy and timeout settings
  • Support for setting certificate path and file to address Net::HTTP errors under Ruby 1.9.2

Thank to…

We’ve had a lot of great contributions in this release.  Huge thanks to chadk for refactoring the HTTPServices, to seejohnrun for the initial implementation of the Batch API, and to amrnt and aselder, who both contributed several patches.  As always, thanks also to Chris, my codeveloper, and to the people at Context Optional, who have been awesome in supporting Koala’s development.

Note: Facebook now requests access tokens for certain API requests

In case you missed this on the FB developer blog, Graph API requests for PROFILE_ID/feed and PROFILE_ID/posts now requires an access token.  This is a breaking change on Facebook’s end.  If you’ve started to see errors when making anonymous requests for those connections, that’s why.

What’s next

If everything goes well with the release candidate, we’ll release 1.1 mid-month.  In the meanwhile, if you encounter any issues or have any questions, drop me an email or open up an issue.
Enjoy the Batch API and have a great week!
Alex
http://github.com/arsduo/koala

Making Rack::Offline useful in uncached mode

In which we play around with mobile.

I’ve been playing around with mobile websites for the last two months — learning about app caches and localStorage, hacking around with jQuery mobile, discovering the limits of mobile devices.  It’s a huge, complicated, fascinating wilderness, and along my way  I’ve come up with or stumbled upon a few solutions that might be useful anyone else heading the same direction.

Let’s start with the app cache and Rack::Offline, one of my first forays into mobile.

How the App Cache works

Anything I could write would just be a rephrasing of Yehuda Katz’s description in the Rack::Offline readme:

The App Cache allows you to specify that the browser should cache certain files, and ensure that the user can access them even if the device is offline.

[snip]

In short, the App Cache is a much stickier, atomic cache. After storing an App Cache, the browser takes the following (simplified) steps in subsequent requests:

  1. Immediately serve the HTML file and its assets from the App Cache. This happens whether or not the device is online
  2. If the device is offline, treat any resources not specified in the App Cacheas 404s. This means that images will appear broken, for instance, unless you make sure to include them in the App Cache.
  3. Asynchronously try to download the file specified in the manifest attribute
  4. If it successfully downloads the file, compare the manifest byte-for-byte with the stored manifest.
    • If it is identical, do nothing.
    • If it is not identical, download (again, asynchronously), all assets specified in the manifest.

Step #4 is the critical step for today’s post.  After loading content from a previously-stored manifest, the browsers checks to see if the old manifest is identical to the new one downloaded from the server; if not, it updates all the files in the cache.

The Problem

Critically, this comparison doesn’t just happen with locally-stored manifests — after the browser downloads an updated cache, it downloads the manifest a second time to verify nothing’s changed. If the manifest was updated during the download, the whole process starts over.  If the manifest is changed every time, the app cache never works.

This is exactly the problem in Rack::Offline’s “uncached” mode (on by default when using Rails::Offline in development mode).  To keep the cache fresh, the middleware calculates a new hash each time:

Digest::SHA2.hexdigest(Time.now.to_s + Time.now.usec.to_s)

Since the browser downloads the cache twice (once at the beginning, and once at the end for verification), the use of usec (microseconds) means the manifest will always be different, meaning you have no app cache.

(In cached mode, by contrast, the gem calculates a hash of the static files in the manifest, ensuring that the cache is reusable until files change.  If you only want to provide static files offline, you can avoid this problem by initializing Rack::Offline with :cache => true; if you want to serve dynamic assets, such as Rails-generated pages, though, this won’t work for you.  See note at the bottom.)

The Solution

Fortunately, it’s fairly straightforward to patch this behavior, as shown in the embedded gist below (look at the uncached_key method in particular).  The cache-breaking comment in the manifest will now will only change after a configurable interval (by default 10 seconds) to allow the browser to download the cache first. There’s always a risk that the download will start too close to the boundary, but since this is development only, you can lengthen or shorten the period as you want by passing :cache_interval => your_interval when initializing your Rack::Offline block.

I’ve submitted this to the Rack::Offline project as a pull request, so hopefully soon this behavior will be built-in.  In the meanwhile, you can throw this into your initializers folder.

If you’ve been working with mobile and offline content, I’d be glad to hear from you — it’s a fascinating subject, and one I’m just starting on.

require "rack/offline/config"
require "rack/offline/version"
require "digest/sha2"
require "logger"
require "pathname"
require 'uri'

module Rack
  class Offline
    def self.configure(*args, &block)
      new(*args, &block)
    end
    
    # interval in seconds used to compute the cache key when in uncached mode
    # which can be set by passing in options[:cache_interval]
    # note: setting it to 0 or a low value will change the cache key every request
    # which means the manifest will never successfully download
    # (since it gets downloaded again at the end)
    UNCACHED_KEY_INTERVAL = 10

    def initialize(options = {}, &block)
      @cache = options[:cache]

      @logger = options[:logger] || begin
        ::Logger.new(STDOUT).tap {|logger| logger.level = 1 }
      end

      @root = Pathname.new(options[:root] || Dir.pwd)

      if block_given?
        @config = Rack::Offline::Config.new(@root, &block)
      end

      if @cache
        raise "In order to run Rack::Offline in cached mode, " \
              "you need to supply a root so Rack::Offline can " \
              "calculate a hash of the files." unless @root
        precache_key!
      else
        @cache_interval = (options[:cache_interval] || UNCACHED_KEY_INTERVAL).to_i
      end
    end

    def call(env)
      key = @key || uncached_key

      body = ["CACHE MANIFEST"]
      body << "# #{key}"
      @config.cache.each do |item|
        body << URI.escape(item.to_s)
      end

      unless @config.network.empty?
        body << "" << "NETWORK:"
        @config.network.each do |item|
          body << URI.escape(item.to_s)
        end
      end

      unless @config.fallback.empty?
        body << "" << "FALLBACK:"
        @config.fallback.each do |namespace, url|
          body << "#{namespace} #{URI.escape(url.to_s)}"
        end
      end

      @logger.debug body.join("\n")

      [200, {"Content-Type" => "text/cache-manifest"}, body.join("\n")]
    end

  private

    def precache_key!
      hash = @config.cache.map do |item|
        Digest::SHA2.hexdigest(@root.join(item).read)
      end

      @key = Digest::SHA2.hexdigest(hash.join)
    end
    
    def uncached_key
      now = Time.now.to_i - Time.now.to_i % @cache_interval
      Digest::SHA2.hexdigest(now.to_s)
    end
    
  end
end

view raw gistfile1.rb This Gist brought to you by GitHub.

Promised side note: it may seem contradictory to serve dynamically-generated assets in an offline cache; it may even be so.  I’m not sure yet.  How to serve a dynamic, interesting mobile site offline, especially one using jQuery Mobile, seems to be a big blank area in the map — one I’m excited to explore :)

Also: there are some issues caching Rails pages in Rack::Offline — something I’ll cover in another post / pull request.

Koala turns 1.0

Hey everyone,

Wooooooooo!

Happy birthday to Koala!  The first version of the gem was released exactly one year ago today, and boy has time flown.  It seems like just yesterday that Koala was 0.4, crawling along with just Graph API support; now, all of the sudden, it’s fully-featured, stable, and one of the most popular Ruby gems for Facebook.  *sniffle*  They grow up so fast!

Since today is Koala’s birthday and all, it seems like a good day to announce that Koala 1.0 has been released. As of today, you can now do everything with Koala that can be done on Facebook: quickly, securely, and, perhaps, with style.

1.0 Features

Koala 1.0 includes massive improvements to almost every part of the gem, giving you new features and more control over existing tools.  Here are the big changes:

  • Photo and file uploads
  • Additional get/put/delete accessor methods to cover all Graph API use cases
  • Support for Facebook’s beta tier
  • Better control over how HTTP requests are made
  • Speed improvements (using http for public requests and api-read where possible)
  • Completely rewritten, faster test suite

Plus a lot more — check out the changelog for the complete list of updates, and the documentation for up-to-date instructions and guidance.

Credits

Over the last year, Koala has become one of the most popular gems for the Facebook API (a close second in downloads and a solid first in Github watchers).  Chris and I want acknowledge, as we hit 1.0, that Koala would be where it is were it not for its awesome user community.  The changelog is a history of improvements both big and small that came via pull request.

We want, in particular, to thank

  • rjacoby, whose advice shaped the gem in its first days and who contributed the test users module
  • fractallian, who contributed the realtime updates module
  • joshk, who refactored Koala’s test suite into a modern, fast machine
  • jtarchie, whose contributions to Typhoeus allowed us to offer photo uploads
  • Everyone at Context Optional for their support and feedback
  • lshepard, without whose challenge to port the Facebook Python SDK to Ruby by the end of last year’s F8 conference none of us would be reading this today
  • waseem, rickyc, pulsd, kbighorse, spiegela, netbe, gilles, imajes, jagthedrummer, and thhermansen, for their patches (if I left anyone out, please let me know)

We’re excited to see others build on top of Koala, like Eukaliptus, a new open-source project from the seasonlabs team that will make it easy to integrate Facebook into a web project using Koala.  We’re putting together a Wiki list of apps and projects using Koala — if you’d like to be on there, just just send us an email or a Github message!

Getting 1.0

Enough talk, let’s install!  To get Koala 1.0, just run

sudo gem install koala

Or add it to your Gemfile:

gem "koala"

Easy.

The Future

Facebook never sleeps, so neither do we: we’re already at work on 1.1, which will introduce Batch Requests, better live tests, and more.

We love to hear your feedback — if you have questions, check out the Koala Users’ Group; if you have a patch, send along a pull request; and if you find a bug, just open up an issue.

That’s it!  Have a great Sunday and enjoy Koala 1.0!

Best,

Alex and Chris (the Koala Team)
https://github.com/arsduo/koala

PS The very professional-looking partying Koala image was captured in nature created using these Creative Commons-licensed works, and is therefore itself available under the Attribution-Share Alike 3.0 Unported license: koala, party hatnoisemaker.

Koala 1.0.0 Release Candidate released!

Hey everyone,

I’m very excited to announce the release of the Koala 1.0 release candidate!  With this version, the 1.0 gem is effectively finished.  Assuming everything goes well, we’re on track to officially launch 1.0 on May 1st (conveniently, the one-year anniversary of the gem’s first public release).
Changelog

So what’s in the release candidate?  Some good stuff:
  • Typhoeus file uploads are now available! This is completely transparent; the only thing you should notice is faster photo uploads.
  • The Koala test suite has been completely refactored and is now massively cleaner, faster, and more awesome (thanks to joshk).
  • OAuth#url_for_oauth_code can now take a :display option (thanks to netbe).
  • Net::HTTP can now accept :timeout and :proxy options (thanks to gilles).
  • GraphAPI.search can now search places, checkins; a query is no longer required (thanks to rickyc).
  • Test users from one app can now be added to another app.
Thanks again to  the Koala user community — it’s fantastic at how many awesome features and fixes have come through pull requests.
The complete diff from beta2.1 is available here.
Installing Koala

To install Koala 1.0rc, just run
gem install koala –pre

As always, let us know how things go and if there’s anything we can help with!

Enjoy!

Koala 1.0.0.beta2 released!

Hey everyone,

We’re excited to announce the release of Koala 1.0 beta 2!  Thank you all for the feedback and ideas that you’ve sent and we’ve incorporated over the last month — thanks to them, Koala is much stronger and easier to use than ever before.

Revised Photo Uploads

With this beta, we’ve revamped photo uploads, making the process simpler by replacing the file hash with a number of convenient options:

# 1) Path to a photo
@graph.put_picture(path_to_my_file)
# 2) Upload a file directly
@graph.put_picture(file_class_object)
# 3) Rails 3 file uploads (ActionDispatch::Http::UploadedFile)
@graph.put_picture(params[:file])
# 4) Sinatra file uploads (Hash)
@graph.put_picture(file_hash)

See more about the new photo interface on the updated Koala wiki.

Important note: the new, final interface is not backwards compatible, so you’ll have to update your code once you upgrade to beta2.  Sorry about that :\

Other Updates

  • Added put_connection and delete_connection convenience methods, which can be used for manipulating albums and provides future compatibility with new Graph API methods.
  • You can now provide an http_options hash to any method as the final parameter (see http_services.rb for more info).
  • You can test your apps against Facebook’s beta tier by passing :beta => true in the http options for any method.
  • TestUser#befriend now requires user info hashes (id and access token) due to Facebook API changes
  • Koala’s APIError is now < StandardError, not Exception

Upgrading

To install the beta, just run sudo gem install koala –pre.  (This seems to work for both 0.10 and 1.0.0.beta1.)

To 1.0

In the next few weeks, we’ll be adding native Typhoeus file upload support and working through the fork and pull queues, which are filled with great ideas.  We’ll probably release one more beta to let you all play with Typhoeus uploads, and once that dust settles we’ll release the final version.

As always, please send us any feedback, comments, and questions!  We’re happy to hear from you.

Best,

The Koala Team
(Alex, Chris, Rafi, and the folks at Context Optional)

Koala 1.0 beta!

Hey everyone,

We’re very excited to announce the beta release of Koala 1.0!  This is a huge release, well deserving of the version bump.  With this update, we’ve expanded Koala to support the entire current Facebook platform and have made improvements throughout the library to make Koala faster, easier, and more stable.

Photo Uploads

You read that right — Koala now supports photo uploads!  We’ve integrated Nick Sieger’s multipart-post gem to provide support through Net::HTTP.  Koala’s Graph API now offers a #put_picture method as well as support for posting file to Facebook through any Graph or REST API method.

# put_picture requires a hash containing info about the photo to upload
file_hash = {
  "path" => 'path/to/some-image-file.jpg',
  "content_type" => 'image/jpeg'
}

@graph.put_picture(file_hash, :message => "I've uploaded a photo!")
# => { "id" => FACEBOOK_UID_FOR_THE_PHOTO }

You can learn more about photo uploads here on the Uploading Photos wiki page.

Photos are in beta

During this beta period, we’ll be working hard to make photo uploads as simple as the rest of Koala.  By the time we officially launch 1.0, you’ll be able to easily upload photos using File objects, temporary uploads to Rails or Sinatra, or from any other source using the same hash system we have now.  We’ll also incorporate native support for Typhoeus file uploads as soon as that’s released (huge thanks to our friend JT Archie for implementing that feature).

Other Enhancements


API Methods:

  • GraphAPI#delete_like: self-explanatory and useful. (Thanks, waseem!)
  • OAuth#parse_signed_request: updated to support Facebook’s current canvas encryption plans.

Internal improvements:

  • For public requests (e.g. no access token), Koala now uses http instead of https to improve speed; this can be overridden globally or per request.
  • Read-only REST API requests now go through Facebook’s faster api-read server.
  • For consistency, GraphAPI#delete_object will now raise an error if there’s no access token (like #put_object and #delete_like).

Test Suite improvements:

  • We’ve expanded the tests for the HTTP service modules.
  • Live tests now verify that the access token has the necessary permissions before starting.
  • We’ve replaced the 50-person network test, which often took 15+ minutes to run live, with a much faster 5-person test.

Installing 1.0.0.beta

Installing the beta is simple — just run

sudo gem install koala --pre

Download the new version and give it a go.  If you hit any problems with photo uploads, the under-the-hood improvements, or anything else, please open a ticket on Github and we’ll get it fixed.  As always, too, if you have any questions/comments/feedback, just drop us an email.

Happy coding!

The Koala Team
(Alex, Chris, Rafi, and the folks at Context Optional)

Test your Javascript!

In  this post, I’ll discuss the whys and hows of Javascript testing; briefly discuss Jasmine, a testing framework; and review some of potential challenges in testing Javascript, which I’ll discuss in more detail in several follow-up posts.

I like Javascript.  The language has some great features that make it possible (even fun) to build powerful, interesting programs; as a programmer, I find few things as rewarding as using the language to create a dynamic UI.  Of course, it’s no secret that Javascript also comes with unhappy features (some from the browsers, others from the language itself) that make using the language tricky and error-prone.  The more Javascript you use, the more likely you are to spend hours painfully debugging some obscure issue in IE7.

There are a few solutions to this dilemma:

  • You could just avoid Javascript — stick instead to simple Ajax calls and let the server handle the rest.  (Unfortunately, that’s a little like cutting off your web browser’s left hand.)
  • You could just write your code and fix problems as they come up.  Why worry now?   (Because you’ll worry later, possibly late one Saturday night night.)
  • You can test your Javascript.  More work upfront, fewer problems down the road.

After a painfully long time firefighting Javascript issues, I’ve come to wholeheartedly embrace testing.  That’s hardly a controversial position, I admit, but it’s one that doesn’t seem as widespread in the Javascript community the way it has for other languages.

Since I’ve spent the last few months writing a few thousand lines of test code, it seems like a good time to pull my thoughts together and share what I’ve learned.  Consider it my holiday gift to all of you :)

The Why

So why test your Javascript?  Well, an easy answers springs to mind:

  1. Because all your code should be tested, duh. Your Javascript is just as legit as your Ruby or your Python or your PHP.  Of course it should have tests.

You don’t have take it only from me that testing is relevant to Javascript: JS ninja John Resig, creator of jQuery, chose to put test frameworks first, before all other subjects, in his excellent new book Secrets of the Javascript Ninja.  He clearly thinks it’s important.

However, I’ll go beyond that one (strong) argument.  I think that testing Javascript code may be even more urgent than testing server-side code, for two reasons:

  1. Because Javascript is front-end. Errors in Javascript are particularly visible and annoying to your users.  Think of it like our old friend the Uncanny Valley — users will more easily accept an obviously broken site (an error page) than a site that looks normal but mysteriously doesn’t work (a form whose Javascript validation is broken).  I don’t have any data to back that up, but I know which one I find more irritating.
  2. Because debugging Javascript is a pian in the ass. If you’ve ever had to reproduce and debug an intermittent problem seen by users on IE, you know what I mean.  Even at their best, Javascript debugging tools aren’t as good as their server-side equivalents; at their worse, it’s just you and alert against the world.  Anything you can do to avoid that fate seems worth it.

Let’s get concrete

Let’s take a quick look at three real-world cases in which testing Javascript has saved me from trouble:

  • Browser differences: my tests recently turned up two cases in which I used a native method (Object.create) not provided in older environments; I also discovered oddities in IE8′s console.log (it doesn’t support .apply()).  Being able to run a test suite on multiple browsers makes it easy to find, reproduce, and solve such problems without spending unnecessary time in IE-land.
  • Corner cases: for an image uploading library, I wrote several (non-deterministic) test cases to smoke out corner cases — for instance, simulating an arbitrary number of uploads that randomly either finished, were canceled, or errored.  These tests were invaluable, unearthing several rare problems that might have been impossible to solve from user reports alone.
  • Code structure: just this month, test-writing helped me identify an overgrown initializer whose expanding DOM setup section warranted its own function.  Though originally spurred by my need to test each part independently, splitting that method has improved the overall quality and maintainability of my code — an example of how tests can lead to better software even before the first test case is run.

Onward!

Now that we’ve talked about why we should be testing, let’s turn those newfound intentions into error-finding assertions.

The How

Last spring I wrote my first tests in JSUnit, a now-defunct port of JUnit to Javascript.  This fall, I went looking for a more modern, better maintained testing framework, and discovered that there are a number of excellent Javascript testing frameworks, including QUnit, used by jQuery and Facebook and YUI Test, from Yahoo.  Many provide familiar features, such as modular test groups and assertion suites; most also offer a browser-based test runner and can be integrated into automated testing.

I ultimately chose Jasmine, a project from Pivotal Labs.  It supports all those features (modular testing, assertions, browser UI, integration into continuous testing), and offers two additional features that won me over:

  • Mocking: Jasmine provides a built-in mocking framework, allowing you to intercept function calls, guarantee their return values, and write expectations against them.  It’s hard to overstate the value of mocking in writing reproducible test cases; it’s the main reason I chose Jasmine.  I’ll provide some examples of mocking below.
  • Syntax: Jasmine uses an RSpec-like syntax, which I find easy and descriptive.  While QUnit breaks modules apart by calling a method between test cases, and YUI Test uses constructors and hashes to define test groups, Jasmine tests follow an easy-to-read structure:
    describe("My Test suite", function() {
      it("should test something", function() {
        // mocks and expectations
      })
    
      describe("a testing subgroup", function() {
        // more tests
      })
    })

After writing over two thousand lines of tests with Jasmine, I’ve found it lives more than meets my expectations.  (Ha!)  So let’s take a look at how it works.

Using Jasmine

If you’ve written tests before (or even if not), Jasmine should be pretty straightforward.  Let’s take a quick look at how Jasmine works — after that, you should check out Pivotal’s documentation, which also covers how to set up a test environment and run the tests in your browser.

describe("Jasmine", function() {
  it("makes testing JavaScript awesome!", function() {
    expect(yourCode).toBe("lots better");
    expect(bugLevel).not.toBe("high");
  });
});

Here, we see all the basic testing ingredients:

  • Test modules: tests are grouped in describe blocks, which can be nested indefinitely deep inside each other.  Each block can have its own setup and teardown methods (see below).
  • Test cases: tests are registered by the it() method, which takes a description and a function containing the test.
  • Assertions: assertions in Jasine come in two parts — expect() and an assertion function, such as toBe().  expect sets up which object or value you’re about to make an assertion about — it can be a variable, raw value, or even a function (see mocking below).  Assertions can be negated by adding .not between the expect and the assertion (as above).

Setup and Teardown

We can also set up setup and teardown functions (beforeEach and afterEach) at any level.  This is actually quite powerful, allowing you to organize test groups with successive levels of specificity (tests for a given method, tests for that method if the interface isn’t yet loaded, etc.):

describe("ImageUploader", function() {
  var imgUploader;
  beforeEach(function() {
    // clean copy each time
    imgUploader = new ImageUploader();
  })

  it("should define a property", function() {
    expect(imgUploader.myProperty).toBeDefined();
  })

  describe("once intialized", function() {
    beforeEach(function() {
      imgUploader.init();
    })

    it("should contain some value", function() {
      expect(imgUploader.anArray).toContain(someValue);
    })
  })
})

Mocks

Mocking is awesome.  In Jasmine, mocks are called spies — you spyOn a function, and can then fake its result and see if it was called.  A few examples:

describe("ImageUploader", function() {
  it("should act a certain way if an image is uploading", function() {
    // easily guarantee you're test runs with the expected conditions
    spyOn(imgUploader, "isUploading").andReturn(true);
    imgUploader.myMethod();
    expect(imgUploader.myProperty).toEqual(someValue);
  })

  describe("when intializing", function() {
    it("should notify the upload manager", function() {
<span style="font-size: 11.8056px;">      // this method will be invoked in init</span>
      spyOn(uploadManager, "register");
      imgUploader.init();
      // validate that a method was invoked as expected
      expect(uploadManager.register).toHaveBeenCalledWith(imgUploader);
    })

    it("shouldn't fire an event if the page is still loading", function() {
      // another method used in init
      spyOn(Page, "isLoaded").andReturn(false);
      spyOn(imgUploader.node, "trigger"); // jQuery trigger function
      imgUploader.init();
      // make sure a function wasn't called
      expect(imgUploader.node.trigger).not.toHaveBeenCalled();
    })
  })
})

Using mocks to control your testing environment is much, much easier than rigging the right conditions each time.  If you’ve never used mocks, you should definitely give them a try.

Gotcha for Rubyists: in Jasmine, the order of method calls is different than in RSpec.  As in Ruby, you set up your spy behavior first, but you call  call expect after executing the relevant code to be tested.

Custom assertions

You can create custom assertions (called matchers) to handle common tests in your code.  It’s fairly simple task — I’ll cover how to write custom matchers, with useful examples, in a follow-up post.

Real code

Want to see some real Jasmine tests?  Just take a look at the test cases I’ve written for some utility functions in my image uploading library.  (If that makes sense, there are a number of other, more complicated tests in the same repository.)

Challenges

This being Javascript, there are some challenges you’re likely to encounter as you write your tests.  Some are self-inflicted, others come from the way the language works.  In particular, I’m going to cover four areas worthy of their own follow-up posts:

  1. Developing reusable test cases: basic test blocks are easy, but you can quickly run into Javascript scoping issues if you want to build dynamic tests.
  2. Testing against browser events: browsers, of course, all behave differently, and you have to do some quick footwork if you want to test code that relies on events. 
  3. Testing encapsulated methods: encapsulation (defining variables in a private scope) is a powerful technique, but it doesn’t sit perfectly well with testing.  I don’t have a surefire solution to this problem, but will discuss my attempts to find a balance.
  4. Overmocking: it’s deceptively easy (I say from experience) to start testing the implementation itself rather than the results, making your tests more brittle.  Avoiding overmocking is mainly a matter of awareness, so I’ll present several examples from my own code.

Keep an eye out for those posts early next year!

Conclusion

That’s Javascript testing in a very brief nutshell.  If it saves even one reader from the pain of an emergency debugging date with IE6, it’s been worth it.

As always, I’d be happy to answer any questions and would love to see your comments.

Hope you’ve all had happy holidays so far — have a fun, safe, and bug-free New Year!

Alex

Koala 0.10 released!

I’m excited to announce the release of Koala 0.10!

New Features

In this release, we’ve added support for Facebook’s new Test User API — huge thanks to Rafi Jacobi, my colleague at Context Optional, for contributing this feature. With the TestUsers API class you can create individual test user accounts:

test_users = Koala::Facebook::TestUsers.new(app_access_token)
# returns the test user's information
test_users.create(is_app_installed, permissions)

You can also make friend connections between your test users, delete them, and so on. You can also create a whole network of test users who are friends with each other using our create_network method. Of course, everything is tested (though the live tests for test user networks take a long time) — all this and more is documented in the Koala wiki.

With 0.10, we’ve also made the access_token property accessible (read-only) and fixed a minor bug.

Upcoming Release

Chris and I are working on support for image and video uploads. We’ve had several requests for this and are eager to add this last feature and hit 1.0, and hope to have it done early next year. (Since neither Net::HTTP nor Typhoeus natively support multipart POST uploads, it’s slightly more complicated than most features.) Stay tuned!

Happy holidays,

Alex

Facebook and Ruby (a presentation)

I gave a presentation on Facbook to the Ruby Users Group here in Munich yesterday.  I’m posting it here in the hopes it’ll be interesting and useful to the community in general.  (This was my second presentation to a Ruby group this year — I gave one on metapgrogramming in July in San Francisco — and I think I’m starting to like presenting.)

In the presentation, I cover the basics of developing web applications with Facebook — what Facebook integration can mean, how the Facebook APIs work, what they can do, and how to use them.  This being a Ruby group and me being the author of the Koala gem for Facebook, I provided some examples of querying the Social Graph using a few lines of Ruby code; I also covered Facebook’s Javascript SDK briefly, since that’s critical to significant Facebook application.

Oh, and I gave the talk in German (don’t worry, the slides are in English).  That was a good challenge.

Enjoy!

Alex



« Previous PageNext Page »
koppel badge miley badge