Over the years I have experimented with different approaches for testing Rails apps. In my early days I used to abuse mocks, a problem I think affected many Rails devs in the early days of RSpec and the BDD movement. Today, I lean towards testing the real thing as much as possible.

I like to consider the test subject a black box. I exercise its interface and check that the results are what I expect. I will use the same approach if I need to test a class, a method, a web screen, an API or a command line tool. The mechanisms will vary, but the approach is the same.

With this in mind, when it comes to testing Rails apps:

  • I like plain unit tests, as a general resource.
  • I love model tests that check results against the database. I like checking results against the database in general (except for system tests).
  • I love system tests and I was delighted by Rails embracing them officially in version 5.
  • I find value in testing helpers, mailers, and jobs.

On the negative side:

  • I rarely write functional (controller) tests.
  • I don’t test routes
  • I don’t test views
  • I prefer system tests to integration tests (using rails terminology, which can certainly be confusing).

I don’t like testing controllers, routes, and views because:

  • System tests will already exercise those components. Any screen will put controllers, routes, and views to work.
  • The cost/benefit is not there for me. They represent a good number of tests to write and maintain. But, at the same time, they don’t increase my confidence significantly. For instance, knowing that a response was a success or a redirect doesn’t tell me much about whether the system works or not. I would love to have them all if they were for free, but they are not.

As per system vs. integration tests, I think the only advantage of integration tests is speed (they are much faster indeed, you can check this experiment I made). I like system tests much better because:

  • I prefer interactions with real screens instead of making synthetic requests for simulating those.
  • They are more realistic and comprehensive. For example, a broken piece of Javascript will make the spec fail, while it will be ignored in an integration test.
  • I love Capybara DSL.
  • I can run isolated system tests in under 2s in my box. That is fast enough for my daily workflow. I rely on cloud test runners with parallelization for large suites. Both things will only get better with time. So I think system tests is a much safer bet if you are starting a new app today.
  • I prefer not having to choose between integration and system tests for the same app. This makes my life simpler.