See all articles
OS Time Travel When Testing Ruby on Rails Web Applications For System-Wide Time Events

OS Time Travel When Testing Ruby on Rails Web Applications For System-Wide Time Events

Recently, we had an interesting request from one of our clients where we needed to implement time travel for testing purposes - or fake time travel, at least! One of our clients' Quality Assurance (QA) team wanted to test functionality under particular time constraints within their application. To do that, they needed to move the application's internal clock to check and see whether some of these time-based actions were allowed or forbidden (depending on the case). Mocking up the operating system's datetime settings on Heroku was the answer, with setting up environmental variables for datetime testing. Heroku datetime testing in a Ruby on Rails application required a little configuration - but we were able to get the job done.

Because the QA team tests full scenarios (using the web interface), we had to ensure that the time remained consistent between multiple requests, so when objects were saved to the database, they stored the correct time with them (i.e., Rails' timestamps).

Libfaketime to the rescue

Since we needed to modify the time for the Ruby process, we decided to use the libfaketime library. The library intercepts various system calls that programs might use to retrieve the current date and time. Thanks to libfaketime, we can easily modify the date & time for our application's processes (Puma and Rails, Sidekiq, workers, etc.) without affecting other applications running on the same machine.

Note: Since it relies on the library preload mechanism of the operating system, there are some cases when it might not work (check the Compatibility issues section of the libfaketime README), but we didn't spot a case where it wouldn't work in our Rails application.

Libfaketime is 3rd party library, you just need to install it (`configure, make, make install`), and you are ready to go. Or are you? Ah, we forgot to mention that our client is using Heroku…

Heroku custom buildpack

If you were using a VPS or self-hosted solution, you would just install the libfaketime library globally and modify the processes needing custom time. On Heroku, it's not relatively that easy - if you need to modify the container's configuration/environment for your app, you must use buildpack.

This means that to make libfaketime available; we had to create a buildpack that downloads and compiles it during the build. We also had to modify the running processes to be wrapped with libfaketime.

However, building a custom Heroku buildpack is a topic for another blog post. Here, we will show how we wrap running processes with libfaketime without modifying the Procfile.

Setting up libfaketime on Heroku

Every Linux system supports the `LD_PRELOAD` env variable. It can contain one or more paths to shared libraries, which may be injected before any other shared library (including the C runtime). Since Heroku heavily relies on environment variables for dyno configurations, it sounded like the perfect solution for us - and indeed it was ;)

So! To get things ready to play with time, we need to set 2 environment variables and use a buildpack to modify the time in the Ruby on Rails application:

  • `LD_PRELOAD` with path set to the `libfaketime1.so` file for injecting libfaketime
  • FAKE_TIME with the relative date to modify the current time

Note: We relied on relative date to simulate time passing. If you specify `FAKE_TIME` to an absolute value (i.e., 2020-12-24 20:30:00), all your further calls to `Time.now` will return the same date and time.

TimeTraveling interface

Since the project already had a dedicated user interface for the QA team to manage testing scenarios (i.e., create new users, etc.), we included a 'Time travel' component. But how can you modify the environment of a running application if it's already running? By using Heroku's API!

Heroku provides a platform-gem that allows you to interact with any Heroku application (i.e., updating environment variables, managing add-ons, builds, and more), where any change to environment variables on Heroku results in immediate container restarts (to pick up the change). Very handy! With the platform-gem, we connected a simple date picker with an environment variable (FAKETIME) value on Heroku. After approximately 30 seconds (time to fully restart Heroku dynos) from the change (using the UI) the QA team was able to move in time. Easy way to conduct time travel testing, right?

It's always a pleasure to make a QA team's life easier. If you are interested in building more robust testing solutions that help your QA team, then speak to us! iRonin can help your project become stronger and better tested than ever before.

Read Similar Articles