Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add "Wait for AJAX" post

  • Loading branch information...
commit 8ffd337a59ce45ccb632171bb3080edbb86dc3e3 1 parent 6d57028
@radar authored
View
44 _posts/2013-07-09-waiting-for-ajax-in-capybara.markdown
@@ -0,0 +1,44 @@
+---
+wordpress_id: RB-342
+layout: post
+title: Waiting for AJAX in Capybara
+---
+
+In Spree recently, we've been using more and more of [Spree's API](http://guides.spreecommerce.com/api) for the Backend component. This means that we've introduced more AJAX-powered features into the backend, which has lead to some interesting test failures.
+
+Some of these test failures are that the tests just aren't waiting long enough for an AJAX request to complete before checking for content on the page. Others are more ... bewildering:
+
+```
+F
+An error occurred in an after hook
+ ActiveRecord::StatementInvalid:
+ SQLite3::BusyException:
+ database is locked: DELETE FROM "spree_activators";
+ occurred at ...lib/sqlite3/statement.rb:108:in `step'
+```
+
+This error happens when an AJAX request is still being processed by the server, but the test finishes and Database Cleaner attempts to wipe the database. The server has locked the database until it's done what it needs to do, and during that lock Database Cleaner attempts to wipe all the data and can't.
+
+To fix this, we just needed to wait for all AJAX requests to complete. This means replacing `sleep` with magic numbers, like this:
+
+```
+sleep(2)
+```
+
+With this method:
+
+```
+def wait_for_ajax
+ counter = 0
+ while page.execute_script("return $.active").to_i > 0
+ counter += 1
+ sleep(0.1)
+ raise "AJAX request took longer than 5 seconds." if counter >= 50
+ end
+end
+```
+
+This code will call `$.active` which is jQuery-code for "how many `$.ajax` requests are still active?", and if that returns more than 0, then it will sleep for a moment, and check again. This code gives AJAX requests 5 seconds to wrap up before raising an exception and moving on.
+
+Use this `wait_for_ajax` method when you need to wait for AJAX requests to finish in your tests to prevent weird, unpredictable JavaScript errors.
+
View
70 _site/2013/07/waiting-for-ajax-in-capybara/index.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Blog of Ryan Bigg - Waiting for AJAX in Capybara</title>
+ <link href="http://feeds.feedburner.com/ryanbigg" rel="alternate" title="The Life of a Radar" type="application/atom+xml" />
+ <link rel='stylesheet' href='/css/style.css' media='screen'>
+ <link rel='stylesheet' href='/css/mobile.css'>
+ <body>
+ <h1 align='center'><a href='http://ryanbigg.com'>The Life of a Radar</a></h1>
+ <div id='page'>
+ <article>
+ <a href="/2013/07/waiting-for-ajax-in-capybara"><header>Waiting for AJAX in Capybara</header></a>
+ <small>09 Jul 2013</small><br>
+ <p>In Spree recently, we&#39;ve been using more and more of <a href="http://guides.spreecommerce.com/api">Spree&#39;s API</a> for the Backend component. This means that we&#39;ve introduced more AJAX-powered features into the backend, which has lead to some interesting test failures.</p>
+
+<p>Some of these test failures are that the tests just aren&#39;t waiting long enough for an AJAX request to complete before checking for content on the page. Others are more ... bewildering:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">F
+An error occurred in an after hook
+ ActiveRecord::StatementInvalid:
+ SQLite3::BusyException:
+ database is locked: DELETE FROM &quot;spree_activators&quot;;
+ occurred at ...lib/sqlite3/statement.rb:108:in `step&#39;
+</code></pre></div>
+<p>This error happens when an AJAX request is still being processed by the server, but the test finishes and Database Cleaner attempts to wipe the database. The server has locked the database until it&#39;s done what it needs to do, and during that lock Database Cleaner attempts to wipe all the data and can&#39;t.</p>
+
+<p>To fix this, we just needed to wait for all AJAX requests to complete. This means replacing <code>sleep</code> with magic numbers, like this:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">sleep(2)
+</code></pre></div>
+<p>With this method:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">def wait_for_ajax
+ counter = 0
+ while page.execute_script(&quot;return $.active&quot;).to_i &gt; 0
+ counter += 1
+ sleep(0.1)
+ raise &quot;AJAX request took longer than 5 seconds.&quot; if counter &gt;= 50
+ end
+end
+</code></pre></div>
+<p>This code will call <code>$.active</code> which is jQuery-code for &quot;how many <code>$.ajax</code> requests are still active?&quot;, and if that returns more than 0, then it will sleep for a moment, and check again. This code gives AJAX requests 5 seconds to wrap up before raising an exception and moving on.</p>
+
+<p>Use this <code>wait_for_ajax</code> method when you need to wait for AJAX requests to finish in your tests to prevent weird, unpredictable JavaScript errors.</p>
+
+ </article>
+ </div>
+ <div id='disqus_thread'></div>
+ <script type="text/javascript">
+ var disqus_shortname = 'ryanbigg'; // required: replace example with your forum shortname
+
+ var disqus_identifier = 'RB-342 http://ryanbigg.com/?p=RB-342'
+ var disqus_url = 'http://ryanbigg.com/2013/07/waiting-for-ajax-in-capybara';
+ </script>
+ <script src='http://ryanbigg.disqus.com/embed.js'></script>
+
+ <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+ <a href="http://disqus.com" class="dsq-brlink">blog comments powered by <span class="logo-disqus">Disqus</span></a>
+ <script type="text/javascript">
+ var _gauges = _gauges || [];
+ (function() {
+ var t = document.createElement('script');
+ t.type = 'text/javascript';
+ t.async = true;
+ t.id = 'gauges-tracker';
+ t.setAttribute('data-site-id', '4e30f771f5a1f547c8000001');
+ t.src = '//secure.gaug.es/track.js';
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(t, s);
+ })();
+ </script>
+ </body>
+</html>
View
132 _site/atom.xml
@@ -4,7 +4,7 @@
<title>The Life of a Radar</title>
<link href="http://ryanbigg.com/atom.xml" rel="self"/>
<link href="http://ryanbigg.com"/>
- <updated>2013-06-26T14:32:23+10:00</updated>
+ <updated>2013-07-09T13:10:00+10:00</updated>
<id>http://ryanbigg.com/</id>
<author>
<name>Ryan Bigg</name>
@@ -13,6 +13,42 @@
<entry>
+ <title>Waiting for AJAX in Capybara</title>
+ <link href="http://ryanbigg.com/2013/07/waiting-for-ajax-in-capybara"/>
+ <updated>2013-07-09T00:00:00+10:00</updated>
+ <id>http://ryanbigg.com/2013/07/waiting-for-ajax-in-capybara</id>
+ <content type="html"><![CDATA[<p>In Spree recently, we&#39;ve been using more and more of <a href="http://guides.spreecommerce.com/api">Spree&#39;s API</a> for the Backend component. This means that we&#39;ve introduced more AJAX-powered features into the backend, which has lead to some interesting test failures.</p>
+
+<p>Some of these test failures are that the tests just aren&#39;t waiting long enough for an AJAX request to complete before checking for content on the page. Others are more ... bewildering:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">F
+An error occurred in an after hook
+ ActiveRecord::StatementInvalid:
+ SQLite3::BusyException:
+ database is locked: DELETE FROM &quot;spree_activators&quot;;
+ occurred at ...lib/sqlite3/statement.rb:108:in `step&#39;
+</code></pre></div>
+<p>This error happens when an AJAX request is still being processed by the server, but the test finishes and Database Cleaner attempts to wipe the database. The server has locked the database until it&#39;s done what it needs to do, and during that lock Database Cleaner attempts to wipe all the data and can&#39;t.</p>
+
+<p>To fix this, we just needed to wait for all AJAX requests to complete. This means replacing <code>sleep</code> with magic numbers, like this:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">sleep(2)
+</code></pre></div>
+<p>With this method:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">def wait_for_ajax
+ counter = 0
+ while page.execute_script(&quot;return $.active&quot;).to_i &gt; 0
+ counter += 1
+ sleep(0.1)
+ raise &quot;AJAX request took longer than 5 seconds.&quot; if counter &gt;= 50
+ end
+end
+</code></pre></div>
+<p>This code will call <code>$.active</code> which is jQuery-code for &quot;how many <code>$.ajax</code> requests are still active?&quot;, and if that returns more than 0, then it will sleep for a moment, and check again. This code gives AJAX requests 5 seconds to wrap up before raising an exception and moving on.</p>
+
+<p>Use this <code>wait_for_ajax</code> method when you need to wait for AJAX requests to finish in your tests to prevent weird, unpredictable JavaScript errors.</p>
+]]></content>
+ </entry>
+
+ <entry>
<title>Finding SQL queries in Rails</title>
<link href="http://ryanbigg.com/2013/06/finding-sql-queries-in-rails"/>
<updated>2013-06-26T00:00:00+10:00</updated>
@@ -1280,99 +1316,5 @@ the one that was one of the, if not <em>the</em> first, hosting company to offer
]]></content>
</entry>
- <entry>
- <title>Deciding what tests to write</title>
- <link href="http://ryanbigg.com/2011/12/deciding-what-tests-to-write"/>
- <updated>2011-12-05T00:00:00+11:00</updated>
- <id>http://ryanbigg.com/2011/12/deciding-what-tests-to-write</id>
- <content type="html"><![CDATA[<p>More people who are new to Ruby are getting into TDD and BDD thanks to the wonderful tools like RSpec and
-Cucumber that make it easy for them to do so. There&#39;s no real public information on exactly <em>what</em> you should be testing, though.</p>
-
-<h3>Testing Validations</h3>
-
-<p>RSpec supports the shoulda-matchers gem and I see a lot of people using this to test validations on their models,
-and this is the <em>first</em> test that they&#39;re writing. I personally think that this is the wrong way of doing things.</p>
-
-<p>The first test should be one that follows the exact steps that the user would need to do in order to approve this
-functionality.</p>
-
-<p>In the example test (written in Capybara&#39;s syntax) below, the user fills in the form (ignoring one of the required fields) and then should see that there&#39;s an error on the page.</p>
-<div class="highlight"><pre><code class="text language-text" data-lang="text">visit root_path
-click_link &quot;Posts&quot;
-click_link &quot;New Post&quot;
-
-fill_in &quot;Title&quot;, :with =&gt; &quot;Deciding what tests to write&quot;
-click_button &quot;Create Post&quot;
-
-within(&quot;#flash_alert&quot;) do
- page.should have_content(&quot;Post could not be created.&quot;)
-end
-
-within(&quot;#post_form .errors&quot;) do
- page.should have_content(&quot;Body cannot be blank&quot;)
-end
-</code></pre></div>
-<p>Or if you prefer Cucumber:</p>
-<div class="highlight"><pre><code class="text language-text" data-lang="text">Given a user is creating a new post
-And the user leaves the post text blank
-When the user clicks &quot;Create Post&quot;
-Then they should see a validation error:
- &quot;&quot;&quot;
- Body cannot be blank
- &quot;&quot;&quot;
-</code></pre></div>
-<p>You can imagine the steps that Cucumber would use, they&#39;d use the Capybara methods just like the original example.</p>
-
-<p>The point is, this test is testing the user&#39;s interaction with the page, which I think is the most <em>critical</em> part
-of the application. If the user cannot see this error message, why does it matter if the model validates the
-presence of this field? <em>It doesn&#39;t.</em></p>
-
-<p>Testing that the model contains the validation is a secondary thing, and is generally something I skip doing. The
-test for at least one of the form&#39;s validations goes into the request spec. If I&#39;m feeling pedantic I&#39;ll write
-another for the other validations, but I can <em>assume</em> that dynamic_form (the thing that provides
-<code>error_messages_for</code> is doing the right thing when it comes to its own methods. If one error message is showing
-up, good chance the others are.</p>
-
-<p>In the case where validation error messages <em>don&#39;t</em> show up then I write a specific test for that inside the
-request spec before going on to fix it wherever I need to.</p>
-
-<h3>Testing Complex Logic</h3>
-
-<p>In <a href='http://spreecommerce.com'>Spree</a>, there&#39;s complex logic involved around orders and tracking
-inventory. This is something I <em>could</em> test with a request spec, but how the system works is made up of so many
-little parts it makes it slow to test the whole thing:</p>
-
-<ul>
-<li>A product exists in the system, has a &quot;count on hand&quot; of 1.</li>
-<li>A user wants to buy this product, so clicks &quot;Add to cart&quot;</li>
-<li>A new order is created in the system, with this item</li>
-<li>User is prompted to sign in</li>
-<li>User is prompted for billing + shipping details.</li>
-<li>User is prompted for credit card details</li>
-<li>User clicks &quot;Confirm&quot; on order page</li>
-<li>Order goes through, deducting 1 from &quot;count on hand&quot; total, bringing it to 0.</li>
-</ul>
-
-<p>A huge portion of these steps aren&#39;t even required to test the count on hand decreasing. What we care about is
-that when an order is placed in the system that the count on hand decreases by one. And so this is where a unit
-test would be better.</p>
-
-<p>This unit test would check that when an order is created, a method such as <code>unstock_items!</code> is called. There would then be another unit test for the actual function of the <code>unstock_items!</code>
-call, ensuring that it goes through the line items for the order and depletes the stock on the products as necessary.</p>
-
-<p>The unit test is going to be much lighter (and quicker to run!) than the request spec, which is just a massive win.</p>
-
-<h3>Conclusion</h3>
-
-<p>At the final retrospective at the CodeRetreat event on Saturday in Sydney, it was brought up that there&#39;s no &quot;right&quot; way to test. Some people thought that testing from the &quot;bottom-up&quot; (unit test first, request specs or similar later) was better, but then they saw the merits of testing &quot;top-down&quot; (request specs or similar first, then unit tests for the fiddly bits) as well.</p>
-
-<p>I think liberal applications of both of these methodologies is <em>one</em> &quot;right&quot; way to test. The more I test, the more I find myself getting better at knowing what to test and how to test it. I
-can see the merits of both ways. I&#39;m preferring top-down though, as that&#39;s, in my opinion, testing what the client is going to be seeing, and that&#39;s what matters most. If there&#39;s a bit of
-gnarly code in there, like the order inventory tracking, then that&#39;s when I&#39;d dive down into unit testing.</p>
-
-<p>What are your thoughts on this?</p>
-]]></content>
- </entry>
-
</feed>
View
2  _site/blogography.html
@@ -29,6 +29,8 @@ <h1 align='center'>The Life of a Radar</h1>
<p>The formatting for earlier posts may be a little skewiff. If you find something like that, <a href='http://github.com/radar/ryanbigg.com'>patches are very welcome</a>.</p>
<ul>
+ <li><a href="/2013/07/waiting-for-ajax-in-capybara">Waiting for AJAX in Capybara</a><abbr>09 Jul 2013</abbr></li>
+
<li><a href="/2013/06/finding-sql-queries-in-rails">Finding SQL queries in Rails</a><abbr>26 Jun 2013</abbr></li>
<li><a href="/2013/02/about-spec-support">About spec/support</a><abbr>02 Feb 2013</abbr></li>
View
43 _site/index.html
@@ -30,19 +30,36 @@ <h1 align='center'>The Life of a Radar</h1>
http://litanyagainstfear.com -->
<div id='page'>
<article>
- <a href="/2013/06/finding-sql-queries-in-rails"><header>Finding SQL queries in Rails</header></a>
- <small>26 Jun 2013</small><br>
- <p>In my work on Spree, sometimes I&#39;ve been wanting to know <em>where</em> queries are coming from. Just like any large codebase, the &quot;magic&quot; that goes on inside it to make the cogs spin the right way can be a bit complex.</p>
+ <a href="/2013/07/waiting-for-ajax-in-capybara"><header>Waiting for AJAX in Capybara</header></a>
+ <small>09 Jul 2013</small><br>
+ <p>In Spree recently, we&#39;ve been using more and more of <a href="http://guides.spreecommerce.com/api">Spree&#39;s API</a> for the Backend component. This means that we&#39;ve introduced more AJAX-powered features into the backend, which has lead to some interesting test failures.</p>
-<p>Active Support&#39;s Notifications feature is really handy for this. All I need to do is subscribe to the <code>sql.active_record</code> event and get it to output the stacktrace of where the query is generated, like this:</p>
-<div class="highlight"><pre><code class="ruby language-ruby" data-lang="ruby"><span class="ss">ActiveSupport</span><span class="p">:</span><span class="ss">:Notifications</span><span class="o">.</span><span class="n">subscribe</span><span class="p">(</span><span class="s2">&quot;sql.active_record&quot;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">details</span><span class="o">|</span>
- <span class="k">if</span> <span class="n">details</span><span class="o">[</span><span class="ss">:sql</span><span class="o">]</span> <span class="o">=~</span> <span class="sr">/INSERT INTO &quot;spree_inventory_units&quot;/</span>
- <span class="nb">puts</span> <span class="nb">caller</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
- <span class="nb">puts</span> <span class="s2">&quot;*&quot;</span> <span class="o">*</span> <span class="mi">50</span>
- <span class="k">end</span>
-<span class="k">end</span>
+<p>Some of these test failures are that the tests just aren&#39;t waiting long enough for an AJAX request to complete before checking for content on the page. Others are more ... bewildering:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">F
+An error occurred in an after hook
+ ActiveRecord::StatementInvalid:
+ SQLite3::BusyException:
+ database is locked: DELETE FROM &quot;spree_activators&quot;;
+ occurred at ...lib/sqlite3/statement.rb:108:in `step&#39;
</code></pre></div>
-<p>Whenever a query that inserts a new record into the <code>spree_inventory_units</code> table is issued through Active Record, this code will give me a complete stacktrace of where that came from.</p>
+<p>This error happens when an AJAX request is still being processed by the server, but the test finishes and Database Cleaner attempts to wipe the database. The server has locked the database until it&#39;s done what it needs to do, and during that lock Database Cleaner attempts to wipe all the data and can&#39;t.</p>
+
+<p>To fix this, we just needed to wait for all AJAX requests to complete. This means replacing <code>sleep</code> with magic numbers, like this:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">sleep(2)
+</code></pre></div>
+<p>With this method:</p>
+<div class="highlight"><pre><code class="text language-text" data-lang="text">def wait_for_ajax
+ counter = 0
+ while page.execute_script(&quot;return $.active&quot;).to_i &gt; 0
+ counter += 1
+ sleep(0.1)
+ raise &quot;AJAX request took longer than 5 seconds.&quot; if counter &gt;= 50
+ end
+end
+</code></pre></div>
+<p>This code will call <code>$.active</code> which is jQuery-code for &quot;how many <code>$.ajax</code> requests are still active?&quot;, and if that returns more than 0, then it will sleep for a moment, and check again. This code gives AJAX requests 5 seconds to wrap up before raising an exception and moving on.</p>
+
+<p>Use this <code>wait_for_ajax</code> method when you need to wait for AJAX requests to finish in your tests to prevent weird, unpredictable JavaScript errors.</p>
</article>
</div>
@@ -51,6 +68,8 @@ <h1 align='center'>The Life of a Radar</h1>
<h2>25 back</h2>
<ul>
+ <li><a href="/2013/06/finding-sql-queries-in-rails">Finding SQL queries in Rails</a><abbr>26 Jun 2013</abbr></li>
+
<li><a href="/2013/02/about-spec-support">About spec/support</a><abbr>02 Feb 2013</abbr></li>
<li><a href="/2013/01/multitenancy-with-rails">Multitenancy with Rails</a><abbr>21 Jan 2013</abbr></li>
@@ -99,8 +118,6 @@ <h1 align='center'>The Life of a Radar</h1>
<li><a href="/2011/10/screencast-pilot">Screencast: Pilot</a><abbr>31 Oct 2011</abbr></li>
- <li><a href="/2011/10/that-one-test">That One Test</a><abbr>19 Oct 2011</abbr></li>
-
</ul>
<center><a href='/blogography.html'>The Complete Blogography</a></center>
</div>
Please sign in to comment.
Something went wrong with that request. Please try again.