Archive for December, 2011

We, Rails developers, have always worried about improving the performance of our test suites. Today I would like to share three quick tips we employ in our projects that can drastically speed up your test suite.

1. Reduce Devise.stretches

Add the following to your spec/test helper:

Devise.stretches = 1

Explanation: Devise uses bcrypt-ruby by default to encrypt your password. Bcrypt is one of the best choices for such job because, different from other hash libraries like MD5, SHA1, SHA2, it was designed to be slow. So if someone steals your database it will take a long time for them to crack each password in it.

That said, it is expected that Devise will also be slow during tests as many tests are generating and comparing passwords. For this reason, a very easy way to improve your test suite performance is to reduce the value in Devise.stretches, which represents the cost taken while generating a password with bcrypt. This will make your passwords less secure, but that is ok as long as it applies only to the test environment.

Latest Devise versions already set stretches to one on test environments in your initializer, but if you have an older application, this will yield a nice improvement!

2. Increase your log level

Add the following to your spec/test helper:

Rails.logger.level = 4

Explanation: Rails by default logs everything that is happening in your test environment to “log/test.log”. By increasing the logger level, you will be able to reduce the IO during your tests. The only downside of this approach is that, if a test is failing, you won’t have anything logged. In such cases, just comment the configuration option above and run your tests again.

3. Use shared connection with transactional fixtures

If you are using Capybara for javascript tests and Active Record, add the lines below to your spec/test helper and be sure you are running with transactional fixtures equals to true:

class ActiveRecord::Base
  mattr_accessor :shared_connection
  @@shared_connection = nil
 
  def self.connection
    @@shared_connection || retrieve_connection
  end
end
 
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

Explanation: A long time ago, when Rails was still in 1.x branch, a new configuration option called use_transactional_fixtures was added to Rails. This feature is very simple: before each test Active Record will issue a begin transaction statement and issue a rollback after the test is executed. This is awesome because Active Record will ensure that no data will be left in our database by simply using transactions, which is really, really fast.

However, this approach may not work in all cases. Active Record connection pool works by creating a new connection to the database for each thread. And, by default, database connections do not share transactions state. This means that, if you create data inside a transaction in a thread (which has its own database connection), another thread cannot see the data created at all! This is usually not an issue, unless if you are using Capybara with Javascript tests.

When using Capybara with javascript tests, Capybara starts your Rails application inside a thread so the underlying browser (Selenium, Webkit, Celerity, etc) can access it. Since the test suite and the server are running in different threads, if our test suite is running inside a transaction, all the data created inside the test suite will no longer be available in the server. Alternatively, since the server is outside the transaction, data created by the server won’t be cleaned up. For this reason, many people turn off use_transactional_fixtures and use Database Cleaner to clean up their database after each test. However, this affects your test suite performance badly.

The patch above, however, provides a very simple solution to both problems. It forces Active Record to share the same connection between all threads. This is not a problem in your test suite because when the test thread is running, there is no request in the server thread. When the server thread is running, the test thread is waiting for a response from the server. So it is unlikely that both will use the connection at the same time. Therefore, with the patch above, you no longer need to use Database Cleaner (unless you are using another database like Mongo) and, more importantly, you must turn use_transactional_fixtures back to true, which will create a transaction wrapping both your test and server data, providing a great boost in your test suite performance.

Finally, if any part of your code is using threads to access the database and you need to test it, you can just set ActiveRecord::Base.shared_connection = nil during that specific test and everything should work great!

Conclusion

That’s it! I hope you have enjoyed those tips and, if they helped you boost your test suite performance, please let us know in the comments the time your test suite took to run before and after those changes! Also, please share any tips you may have as well!

This article is about our Hacking Evenings, a weekly session in which we gather our team and do something to improve our knowledge. We talked about it on Ruby Conf Brazil and Ruby Conf Uruguay. You can see the slides here:

We’ve always worried about how to disseminate knowledge inside PlataformaTec. As a company with 13 employees (8 developers, 1 designer, 2 business analysts and 1 project manager), we all know that someone always has something new to share and that everyone else can learn from it.

As regular speakers in Agile and Ruby events, we usually rehearse our talks with our colleagues in order to get some feedback and improve them. But those rehearsals happened at best once a month.

Some months ago I read Chad Fowler’s Passionate Programmer book. In this book, Chad said about Brown Bag sessions, which is a lecture or talk given at lunch time (the name “Brown Bag” comes from the food packet that people usually brought, usually brown bags). Reading this chapter was the motivation I needed to gather our team and discuss the requirements to make our own meetings happen.

The first requirement is inherent to a consultancy company which sometimes has people working from its clients’ offices, making it harder to find a day and hour when everyone can participate. After some discussion, we decided to do that on tuesday evenings. When we decided to do that most of our team could participate but, as time goes by, the ones that couldn’t changed their schedules to fit our hacking evening there.

The second requirement was (IMHO) easier to solve: the resources. We have a big flat TV in our office since september, but before that we had only 24 inches monitors which were also fine and that you probably already have in your office. We also needed motivated people to spend some time preparing something to share. As a company that always looks for mastery, this was not hard and since we started someone always had something to share (but in case nobody has, we can watch a screencast or a talk at confreaks, which requires almost zero preparation).

The third requirement is to have something to eat. Because it is after the working hours, people are usually starving, and we can’t have fun in our hacking evenings without grabbing something to eat. In first sessions, we ordered some Pizza, which was great but expensive. So, we decided to prepare something on our own. Before starting we usually go to a near supermarket and buy the ingredients. We already prepared ham and cheese sandwiches, hot-dogs, cheeseburgers and tapioca (a brazilian wrap made of manioc).

OK, we’ve satisfied our requirements. But how to keep people motivated and participating on every session? Here are some tips:

  • Make it a habit: if we do it every week at the same day and time, it is easier for everyone to commit themselves to participating. Ok, not everybody will go every week, but once we introduced this habit into our colleagues minds, they will avoid scheduling other activities in the same time and participate
  • Accept any idea: the main idea is to spread knowledge. Can it be a typical talk? yes. Can it be a design or agile workshop? Yes. Can it be an open-source hacking session with someone helping the others to do bug-fixes and developing new features? Yes.
  • Invite friends to talk and watch: If you have a friend that knows a lot about some subject and can improve the knowledge of your company, why not call him to participate ?
  • Have fun: this is, by far, the most important tip: without having fun, people will not participate. Try to make it fun to make people spend a nice time. We’re talking about after-hours so, if it is not fun, people will not participate.

How do you disseminate your knowledge internally? Do you usually do Brown Bag Sessions? What activities do you usually do ? Would you recommend an activity for us?