{"id":1792,"date":"2011-12-13T13:41:37","date_gmt":"2011-12-13T15:41:37","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=1792"},"modified":"2012-03-08T10:06:41","modified_gmt":"2012-03-08T13:06:41","slug":"three-tips-to-improve-the-performance-of-your-test-suite","status":"publish","type":"post","link":"https:\/\/blog.plataformatec.com.br\/2011\/12\/three-tips-to-improve-the-performance-of-your-test-suite\/","title":{"rendered":"Three tips to improve the performance of your test suite"},"content":{"rendered":"

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

1. Reduce Devise.stretches<\/h3>\n

Add the following to your spec\/test helper:<\/p>\n

\r\nDevise.stretches = 1\r\n<\/pre>\n

Explanation: <\/strong>Devise uses bcrypt-ruby<\/a> 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.<\/p>\n

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<\/code>, 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.<\/p>\n

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!<\/p>\n

2. Increase your log level<\/h3>\n

Add the following to your spec\/test helper:<\/p>\n

\r\nRails.logger.level = 4\r\n<\/pre>\n

Explanation: <\/strong>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.<\/p>\n

3. Use shared connection with transactional fixtures<\/h3>\n

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:<\/p>\n

\r\nclass ActiveRecord::Base\r\n  mattr_accessor :shared_connection\r\n  @@shared_connection = nil\r\n\r\n  def self.connection\r\n    @@shared_connection || retrieve_connection\r\n  end\r\nend\r\n\r\n# Forces all threads to share the same connection. This works on\r\n# Capybara because it starts the web server in a thread.\r\nActiveRecord::Base.shared_connection = ActiveRecord::Base.connection\r\n<\/pre>\n

Explanation: <\/strong>A long time ago, when Rails was still in 1.x branch, a new configuration option called use_transactional_fixtures<\/code> 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.<\/p>\n

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.<\/p>\n

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<\/code> and use Database Cleaner to clean up their database after each test. However, this affects your test suite performance badly.<\/p>\n

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<\/code> back to true, which will create a transaction wrapping both your test and server data, providing a great boost in your test suite performance.<\/p>\n

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<\/code> during that specific test and everything should work great!<\/p>\n

Conclusion<\/h3>\n

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!<\/p>\n","protected":false},"excerpt":{"rendered":"

Three quick short tips to improve the performance of your test suite!<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[94,36,59,96],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/1792"}],"collection":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=1792"}],"version-history":[{"count":11,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/1792\/revisions"}],"predecessor-version":[{"id":2586,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/1792\/revisions\/2586"}],"wp:attachment":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=1792"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=1792"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=1792"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}