Friday, November 16, 2012

Speeding up our test suite in development

Speed gauges
Image by Juan-Calderon.

When you practice test-driven development, most of the time you need to run only a small number of tests to validate your recent code changes. Unfortunately things change once you start refactoring. Refactoring models implicates your entire application. So in order to keep things from going down the drain, you’ll need to run all (or most of) your tests constantly. And that’s where things get tedious.

At Railsonfire we develop our Ruby on Rails web app test-driven with Rspec. Most of our specs are high level request specs (in a way integration tests) which are slow compared to controller or model specs. One of the reasons for this slowness is that request specs test the whole application stack. Another reason is that we changed the default capybara driver from Rack::Test to Poltergeist.

“Poltergeist? But that’s so much slower!”

Right! But we chose Poltergeist because it tests a web application more like a user experiences it. While Rack::Test only considers the HTML (or whatsoever) response from your application, Poltergeist launches a headless WebKit browser, provided by PhantomJS.

This way we could not only test our backend application by navigating through the HTML code, but also test our awesome Javascript sugar.

“Ok, so use Poltergeist only when your specs require Javascript!”

That would be enough to test all of the Javascript functionality. Unfortunately Javascript can be a villain, too. If there’s a Javascript error, chances are that even things stop working that normally wouldn’t rely on Javascript. If Poltergeist comes across a Javascript error it will throw an exception immediately and thus fail the spec.

Because of this additional safety we decided on making Poltergeist the default capybara driver.

Development vs. Railsonfire CI

Using test-driven development will eventually take you to the point of misery, when running all your specs slows down your productivity unbearably. Poltergeist lets us reach this point at record speed. So what to do now? On the one hand we wanted to retain the quality of our web app, on the other hand we wanted to continue developing without feeling the need of poking our eyes out while waiting for the specs to succeed.

Running our spec suite on my computer with this setup took 8:45 minutes.

The solution was Railsonfire itself: We would speed up the specs in development as much as possible and let the Continous Integration server do all the cumbersome work.

Step one: Use Poltergeist only for Javascript

I know, that’s what you always said. We decided on using Poltergeist for all request specs that required Javascript, and Rack::Test for the rest in development. On the continous integration server we’d still run all specs with Poltergeist.

This little change reduced the execution time to 5:00 minutes.

Step two: Skip slowest specs

There were some specs that took up to 40 seconds to run. These were used to check some quite complicated procedures that rarely changed. We decided to skip all specs for development that took longer than 10 seconds.

Rspec provides tagging to achieve this: We tagged our slow tags with speed: "slow". Running our specs with rspec --tag ~speed:slow reduced the execution time by half: 2:29 minutes. Yei!

Step three: Skip remote specs

In Railsonfire we integrate a couple of external services like Github. Of course we also needed to verify that the communication with these services worked. But a developer’s life is hard: Sometimes you are on a train, on a plane or there’s simply no network reception. And all of a sudden many of your specs fail.

Therefore we tried to remove the dependencies on external services for as many specs as possible. We tagged all specs that still required internet access with remote: true and skipped them in development.

Executing rspec --tag ~@remote --tag ~speed:slow now finished in: 1:28 minutes.

This stunning result was good enough for now, so we stopped optimizing here. But for sure we well cover further optimizations in future blog posts.

Results

By changing our test setup and skipping time-consuming tests in development we were able to cut down test execution time by more than 83%. This way we could stay productive during development and still perform extensive checks on our web application using the Railsonfire continous integration server.

Here’s a final overview of the results after each optimization step:

  number of specs execution time (minutes)
All tests with Poltergeist: 128 8:45
All tests with Rack::Test/Poltergeist: 128 5:00
Without slow specs (> 10s): 107 2:29
Without slow and remote specs: 99 1:27

This is my first blog post written for Railsonfire. Find the original post at the Railsonfire blog.

Monday, January 16, 2012

Talk on Rails asset pipeline

I’m giving a talk about the Rails asset pipeline today in the evening.

However, this talk will be interesting not only to Rails guys but to all kinds of web application developers: I will focus mainly on how assets like JavaScript and CSS should be handled in modern web application frameworks. Ruby on Rails will just serve as an example of many best practices of asset handling.

Details

Monday, January 16, 7pm-9pm
University of Technology Vienna, Freihaus HS4

Note that there will be another talk before mine (jsug.at/wiki/Meeting_45) which can be interesting for you too. The talks will be held in German.

Tuesday, January 10, 2012

Ruby Float quirks

Working with Float values sometimes really leads to unexpected behavior. Take the following example:

def greater_than_sum?(float1, float2)
  difference = float1 - float2
  difference + float2 > float1
end

You would expect this method to return false all the time. But try calling greater_than_sum(214.04, 74.79) and it will return true!

The problem

Open up an irb shell and type 139.25 + 74.79, then you’ll see the reason for this quirk: The result is 214.04000000000002, which is apparently slightly greater than 214.04.

Float numbers cannot store decimal numbers precisely. The reason is that Float is a binary number format and always needs to convert from decimal to binary and vice versa.

As you probably know, not all numbers can be represented in decimal format. When you calculate 1/3, the result is 0.33333333333333…. An endless chain of 3s.

The same holds true for binary numbers. When a decimal number is converted to binary format, the resulting binary number might be endless. But since we don’t have endless memory on our computers to represent the number, the computer cuts off that number at some point. This produces a rounding error and that’s exactly what we have to deal with here.

The solution

In our previous example you can add a second condition to check if a number is within a certain interval around another number, called delta:

def greater_than_sum?(float1, float2)
  delta = 0.0001
  difference = float1 - float2
  difference + float2 > float1 && difference + float2 - float1 < delta
end

What you choose as delta is pretty much up to you. It depends on the precision you need in your comparisons and the expected error: Unfortunately the calculation error increases the more calculations you perform on a number.

Decimal solution

If you need an exact comparison of 2 decimal values, you might wanna go with BigDecimal. All calculations on decimal numbers are much more accurate than with Floats.

So I just use BigDecimal instead of Float from now on!

Of course you can do that, but there is one big disadvantage: Calculations on BigDecimals are considerably slower. The floating point logic that Floats use is implemented directly in hardware on your computer’s processor already. This makes it blazingly fast. BigDecimals on the other hand are just a software implementation to cope with Float’s issues with no direct hardware support. More on this on Wikipedia.

So whenever it is possible use Floats – that’s why they are default. When you need precise decimal numbers, e.g., because you are working with currencies, go for BigDecimal.

If you want to dig a little deeper check out What Every Computer Scientist Should Know About Floating-Point Arithmetic. Enjoy ;)

Update: Jarmo Pertman compares Floating point arithmetics in 19 programming languages and proposes a general solution for Ruby using BigDecimals for Float operations. However, be aware of the performance concerns of his solution.

Update 2: Kyrylo Silin created a Russian version of this article.

Friday, December 2, 2011

To link or not to link - Rails link helpers unveiled

One of the first things you learn when you create Action View templates in Ruby on Rails is how to link to other urls and actions. The basic syntax is

link_to 'Change my life', edit_life_path(@my_life)

But maybe you haven’t heard of link_to’s little brothers and sisters that come in handy really often:

I will cover each of these underestimated little friends in the following:

This helper renders the link only under certain circumstances, otherwise it just shows the link text.

Given you let your logged in users do some magical stuff. But even when they’re not logged in you want them to know that it is possible to do some magical stuff:

link_to_if user_logged_in?, "Do magical stuff!", magical_stuff_path

This way you show logged in users a Do magical stuff link

<a href="/magical_stuff">Do magical stuff</a>

and other users just a plain Do magical stuff text.

Admittedly, some users might get annoyed trying to do magical stuff without having a clue how to. Fortunately Action View provides a solution:

link_to_if user_logged_in?, "Do magical stuff!", magical_stuff_path do
  link_to "Log in to do magical stuff", log_in_path
end

The block is executed in case the condition is not met. So if user aren’t logged in, you give them a hint how to do magical stuff. Now everybody’s happy eventually.

The counterpart to link_to_if is link_to_unless. You can use the simple version that will either render a link or just text

link_to_unless drunk?, "Have a beer", drink_beer_path

but also the advanced version using a block:

link_to_unless drunk?, "Have a beer", drink_beer_path do
  link_to "Dance like crazy", crazy_dancing_path
end

This special variant of link_to_unless renders a link unless it links to the current page. The main purpose of it is a navigation:

<nav>
  <ul>
    <li><%= link_to_unless_current 'Go back home', home_path %></li>
    <li><%= link_to_unless_current 'Get cookies', cookies_path %></li>
  </ul>
</nav>

When you’re on the home page, Go back home is rendered as plain text whereas Get cookies is still a link to delicious cookies_path:

<nav>
  <ul>
    <li>Go back home</li>
    <li><a href="/cookies">Get cookies</a></li>
  </ul>
</nav>

Also link_to_unless_current accepts a block, so we could reduce the above code to

<nav>
  <ul>
    <li>
      <%= link_to_unless_current 'Go back home', home_path do
        link_to_unless_current 'Get cookies', cookies_path
      end %>
    </li>
  </ul>
</nav>

I hope you found this useful, if you have any questions or suggestions just leave me a comment!

Saturday, November 19, 2011

Internationalize Haml-Templates with haml-i18n

I love Haml for making my html templates more readable. Unfortunately this comes to an end when I use internationalization. I end up with markup like

  %p= I18n.translate('.user_name')

To overcome this issue I created haml-i18n. Using haml-i18n instead of the above code you can just write

  %p User name

Haml-i18n will try to look up the I18n key
  • [view_folder].[template_name].user_name
  • or user_name if the template-scoped translation doesn’t exist.
If none of these keys exist, the haml template just acts as usual: it renders 'User name'.

I just created haml-i18n yesterday to fit one project’s needs, therefore it is still very basic. If you have any suggestions on how to improve it, please leave a comment.

Sunday, May 10, 2009

Assignments, Increments, Decrements,... in ActiveRecord

Maybe you've already come across the fact that if you want to set an attribute of an ActiveRecord model within a method, the following code won't work:

  def enable!
    enabled = true
    save!
  end

The problem is that instead of calling the method enabled= a local variable enabled is initialized and set true. Since the scope of this variable is only the method itself, the state of the object isn't altered and therefore nothing changes when saving the model.

To avoid this behaviour, you have to call the method on the object itself explicitly:

  def enable!
    self.enabled = true
    save!
  end

So far so good. But what I found quite surprising is that the same applies to all operators containing an assertion. So instead of writing

  def double!
    price *= 2
    save!
  end

you have to use

  def double!
    self.price *= 2
    save!
  end

Admittedly — if you think about it a little it's not that surprising anymore, since the method is nothing but the short form of

  def double!
    price = price * 2
    save!
  end

which makes the problem obvious again: While the right price in the term refers to the getter method, the left one is just a local variable again.

The same applies for all operators making use of a ...= setter method, e.g. +=, -=, /=, etc.

About Rails Troubles...

Since about 2 years I'm using Ruby on Rails , but sometimes I'm still feeling totally like a beginner. I spend hours on finding solutions for apparently trivial problems and once I have found them I'm really doing a great job in forgetting them as fast as I can.

Therefore this blog has 2 main purposes: Saving you the effort of struggling with the same problems as me and reminding me of the solutions I once came up with but I already forgot about.

If you have any ideas how to make things better, feel free to comment the posts! Like everybody I'm still learning and I'm happy about fellows that share their wisdom with me!

Special thanks to Ryan Bates from railscasts.com Рyour screencasts are a huge inspiration and motivation for my work and for this blog Рand to Thomas H̦chtl from media24 Рyou infected me with the rails-mania!

 
DreamHost coupon code