On Rails testing
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 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.