After reaching the point where your product has grown to a large scale application, you probably start to ask yourself - why the hell do are all my tests executions so long?!
After reaching the point where your product has grown to a large scale application, you probably start to ask yourself - why the hell are all my tests executions so long?! This is really important whether you’re developer or a product owner. No matter who you are, execution time is key to developing and deploying an app with ease. Here at Netguru, we’ve this issue many times and we’d like to show you how we handle it:
Use RSpec 3 and Rails-free configuration
Aside from all its awesome features, we love RSpec 3 because it means you don’t require
rails_helper.rb in each file. Each spec can either include
rails_helper.rb, which dramatically impacts on execution time. While testing APIs (as well as models and such) you don’t have to load the whole Rails framework. This results in shorter execution time. It’s really simple to save time this way.
Have less dependencies
Did you know that, while running a test, your gems are preloaded as well? You usually don’t need most of them (although, it is sometimes really useful, especially when you need to use extra classes). The simplest way to limit the number of loading gems are groups. The purpose of having groups is that it allows you to specify groups of dependencies to be used in certain environments, but not in others. Simply check your Gemfile and split gems into groups (e.g. development, test, production etc.). This will speed up the specs depending on how many extra gems you loaded.
Mock and stub
Stubbing and Mocking are great features we all use when writing specs. If you don't want an execute method that takes up time (like an API request or something), you can temporarily set a value it returns. By stubbing methods on objects, you let them return a value you set. The methods are not called in this case, and the value is accessible for the asking.
For many operations, you don’t need a DB connection at all. So, stop saving tested objects into the DB and build them instead. The DB operations are one of the most time consuming in tests and are totally unnecessary in most cases. In fact, except for some weird edge cases, only controllers’ specs need to save some objects into DB. Think about it next time you try save an object only to call a function on it or pass it into a method.
Use factories or fixtures
Want to speed up your test suite? Reduce the number of objects persisted to the database. With
Factory Girl, this is really easy; instead of using build or create to instantiate your models with data, use
build_stubbed! Know that
build_stubbed is the younger, more hip sibling to build. It instantiates and assigns attributes just like build, but that’s where the similarities end. It makes objects look like they’ve been persisted, creates associations with the
build_stubbed strategy (whereas build still uses create), and stubs out a handful of methods that interact with the database and raises if you call them. This leads to much faster tests and reduces your test dependency on a database.
Use a fast test framework
If you don’t need to use the whole of RSpec functionalities, it might be better to consider using a minitest library, especially when writing small gems or libraries. Minitest and fixtures are already built into Rails, so setup is trivial and it works out of the box. There is a great post about doing lightweight testing by Brandon. Go check it out - Why I'm Sticking With MiniTests & Fixtures in Rails and a great benchmark table here: Bow Before Minitest.
Upgrade your Ruby to 2.1
Version 2.1 introduced good performance over 2.0 when it comes to Garbage collector. Obviously, it has a strong impact on specs and decreases the time of execution. Based on isrubyfastyet, Ruby 2.1 saves your hardware’s memory and requests are made way faster. Boot up time also improved. This, again, will save you milliseconds. So go and update!
There are two cools gems that I want to share with you. The first is Spring. Spring is added to your gemfile by default from Rails 4.1, but you can add it manually as well. It’s notable for developing large scale applications, but its also useful for standard apps as well. Each time you run rake tasks, the whole application is reloaded. Spring allows you to prevent that and reloads only the classes that really need to be reloaded. It automagically detects changes in code which is super useful. Another gem we use all the time is Guard. Guard runs in the background, running specs for you based on the file you’ve just changed. So basically, once you edit your model, Guard will run specs for that model. You can also set it up to run all specs within the project. This saves you time when working on a local machine: you don’t have to switch between console and editor all the time.
Test execution can take a lot of time. Along with the reasons we mentioned above, there’s also a chance that you aren’t using the full potential that’s in your hardware. By default, all the specs are running in a single process which is not effective at all. You can try to speed them up by adding the
parallel_tests gem. It sets up your test environment by running test in multiple threads or CPUs. It allows you to split tests into groups and run in a single process with its own database. This is commonly used with continuous integration tools like CircleCI, but you can use it on your local machine as well. It really works and speeds up your tests, depending on your machine power.
If your goal is to make the project better, faster, and more secure, make sure to read our Quick Guide to Code Review as well!