Caching Support

semperos edited this page Sep 7, 2012 · 13 revisions

Selenium-WebDriver provides some developer-facing caching facilities, specifically when leveraging their built-in PageObject design pattern API. However, this does not mesh with Clojure as naturally as it does in the Java API.

For this reason, clj-webdriver provides element caching on a per-page basis via Clojure's core.cache library. Read on for usage details.


By default, clj-webdriver allows you to initialize an empty element cache by passing a "cache spec" to the new-driver function. This spec requires the following bits of information:

  • The caching strategy that should be used (all strategies supported by Fogus' clache library are supported by clj-webdriver)
  • Arguments that should be passed to the respective clache constructor for the caching strategy chosen
  • Either an :include or :exclude seq (vector), which should contain cache rules for inclusion or exclusion (whitelisting or blacklisting respectively)

Here is an example from clj-webdriver's unit tests, showing the initialization of a new Driver record using the :basic cache strategy, no additional arguments (because BasicCache doesn't take any) and a couple of :include rules:

(def dr (new-driver
           {:browser :firefox,
            :cache-spec {:strategy :basic,
                         :args [],
                         :include [{:css "ol#pages"}]}}))


For a list of available strategies, take a look at the core.cache library. In terms of the storage strategy, clj-webdriver simply uses an atom that wraps one of the supported cache types. By specifying the :strategy and by providing a vector of :args to feed to the constructor of that strategy (again, look at the core.cache API), clj-webdriver will instantiate the appropriate store inside the :element-cache attribute of your Driver record.

Here are the keyword shortcuts for the currently-available caching strategies:

  • :basic - A basic cache
  • :fifo - First In First Out cache
  • :lru - Least Recently Used cache
  • :lirs - Low Inter-reference Recency Set replacement cache
  • :ttl - Time To Live cache
  • :lu - Least Used cache

Unless you have specific needs to the contrary, you're probably safe just using :basic. Depending on your application, a time-based :ttl cache might also be appropriate, if you elements change over determined periods of time on a given page load. Considering the amount of data generally displayed on a single web page, the other strategies are probably "overkill" in terms of their algorithms for efficiently storing and returning cached values.

Cache Rules

Cache rules are the heart of the clj-webdriver cache system. If caching is enabled (i.e. you've defined a cache strategy), then the find-element function automatically checks for cache rules in your :cache-spec and performs caching automatically based on those rules. You must choose either a whitelisting approach (:include) or a blacklisting one (:exclude). For either approach, your :include/:exclude entry consists of a vector of individual cache rules. See the above code snippet for an example.

These cache rules can be one of the following:

  • A map. If you think about the find-element function, then a map represents a find-element query. For the sake of consistency, map entries must match exactly to enable/disable caching for a given map structure.
  • A vector. Again, thinking back to the find-element function, a vector represents an ancestry-based query. Just like a map, this query must match exactly.

Accessing the Cache Directly

The protocol used to implement clj-webdriver's caching support is exposed publicly, so you can manipulate the cache safely by looking at the IElementCache protocol and calling its functions. These functions massage different Clojure data structures to provide uniform keys for insertion and retrieval, due to the complexities of the find-element function's parameters. For this reason, CRUD-style operations should be executed via the protocol functions and not via standard Clojure hash functions, unless you have a good reason to do otherwise.