RailsConf 2016
Secrets of Testing Rails 5 Apps

Secrets of Testing Rails 5 Apps

by Prathamesh Sonpatki

In the video titled "Secrets of Testing Rails 5 Apps," Prathamesh Sonpatki discusses the enhanced experience of testing applications built with Ruby on Rails 5. He begins by introducing himself as a director at Big Binary, a Ruby on Rails consulting company, and shares his enthusiasm for stickers and open-source contributions.

The main focus of the session is the new features and improvements in testing Rails 5 applications, particularly the introduction of a new test runner which simplifies the process of running tests. Key points from the session include:

  • New Test Runner: Rails 5 introduces a new test runner that replaces the previous rake test command with bin rails test, allowing for better rerun capabilities, documentation, and user experience.
  • Colored Output: The new runner provides colored output for test failures, enhancing clarity and user engagement while looking through test results.
  • Integration Tests Emphasis: The shift towards integration tests in Rails 5 means that the framework now accounts for the entire middleware stack when executing requests, offering a more realistic testing environment compared to the previously used controller tests.
  • Backward Compatibility: Existing functional tests are not broken by upgrading to Rails 5; however, all new tests default to integration tests.
  • Testing API Apps: The introduction of API-only applications makes it easier to handle JSON and XML data. The video discusses how Rails 5 simplifies the testing of these APIs by managing response formats and making assertions clearer.
  • Randomized Test Order: Tests now run in a randomized order by default, which improves parallel testing capabilities and ensures that tests do not depend on order for their success.
  • Fixtures and Metadata Improvements: Changes in fixtures allow for direct referencing and loading of model class data, simplifying the setup in legacy applications.

With these enhancements, the overall testing process in Rails 5 has become faster, more efficient, and aligned with modern development practices. Prathamesh concludes by encouraging viewers to embrace the new capabilities of Rails 5 testing, while also mentioning the availability of resources such as a blog series and a newsletter for ongoing learning. He wraps up the session by referring to other tools like RSpec for those interested in further enhancing their testing strategies.

In summary, the key takeaways emphasize the significant strides Rails 5 has made in streamlining the testing process and enhancing developer experience, while also accommodating modern application requirements.

Prathamesh's enthusiastic approach reinforces the importance of quality tests in software development, ultimately contributing to more robust and maintainable codebases.

00:00:09.650 Hi everyone! Today we are going to see how we should test Rails 5 apps. My name is Prathamesh, and this is my Twitter handle. I work for Big Binary, where I am a director at a Ruby on Rails consulting company that also specializes in React and React Native.
00:00:17.340 If you want to discuss Rails, React, or React Native, we can chat about it later. I like stickers, like everyone else, but I'm a bit different. While most speakers give away stickers to attendees, I prefer to collect them. This is my laptop, which I have adorned with stickers I got from RailsConf.
00:00:29.970 If you have any stickers to share, please contact me later because I want to add them to my collection. I also help maintain the site code3l.com, which was started by Richardson Women, the recipient of the Dear Ruby Hero Award this morning. This site helps you get started with open-source contributions. If you subscribe to this site, it will send you emails with some issues on GitHub, so you can start contributing.
00:00:55.800 If you're interested in contributing to open source, please subscribe to the site. We can also discuss it after my talk if you want any more details. I am also part of the Rails Issues Team. As part of this team, I try to help with opened Rails issues. If you’ve opened any Rails issues, you might find me commenting there or closing issues that are no longer valid.
00:01:19.830 Everyone is excited about Rails 5, right? There are so many new features, like Action Cable. We just had an awesome talk about Action Cable, but besides that, we have API-only apps, and we support Ruby 2.2. All of these new features mean we also have to test them. We need to know how to test all of these new changes, and there are many changes related to testing.
00:01:41.460 Basically, with Rails 5, there are many features, as well as changes related to how we test our code and how we run our tests. Today, we will explore all of those changes and see what has changed and how to write tests. We'll begin with running tests, and in the second part of the talk, we will learn how to write tests.
00:02:05.280 Now, before I discuss how to run tests in Rails 5, let’s do some recap. Before Rails 5, the only way to run tests was through the rake task with the command 'rake test.' I will only focus on the default Rails test task, not touching on RSpec or any other tools. By default, we could run our tests using 'rake test,' which runs all the tests in our application.
00:02:33.780 If you wanted to run specific tests, like those for controllers or models, you had specialized rake tasks. If you had additional folders, like test workers or test services, you could extend those tasks by writing your own rake task to run those tests. However, in comparison to other testing tools, this approach has some limitations.
00:03:03.000 For instance, if I had a test suite that was always passing, I wouldn't need any errors in the test. I could just use 'rake test,' and it would pass without needing to do anything. But that is not always the case. The normal workflow involves writing a test that fails, then writing some code to make that test pass, and then writing the next test.
00:03:31.889 This was the typical process we followed in our day-to-day work before Rails 5, where we'd test a controller and see printed output. If I'm able to use this output to run the test again, it would obviously help improve my testing workflow. This previous output would inform me which test failed, allowing me to see that the test on line number 9 from the Users controller had failed.
00:04:00.000 However, reusing this information to rerun tests didn’t work because Rails couldn’t understand how to rerun the test again, which was a limitation prior to Rails 5.
00:04:19.800 To resolve these kinds of limitations, Rails 5 introduced a new test runner, which will help us rerun tests and create a proper workflow for running our tests. This new test runner is utilized with the 'bin rails test' command, marking a significant change in Rails 5.
00:04:53.310 When using 'rails -L,' we see a command for 'rails test,' which is quite different from the rake task. It operates as a proper executable that supports various arguments and comes with its own documentation, as we will see later. The output from 'bin rails tests' on a Rails 5 app shows that it has finished testing, and while the output might not seem significant now, we will see how it enhances the testing experience.
00:05:28.300 Let's look at how we can rerun specific tests. Using the same example from earlier with 'rake test' and controller tests, the output indicates which test failed along with the line number but also includes a command ahead of it. I can just copy that 'bin rails tests' command and the test that failed, and if I run this particular command, it will rerun only that specific test.
00:05:54.130 This capability allows for easy copying and pasting to run only those tests that failed. Additionally, this new test runner has better documentation. Before Rails 5, with only the rake task available, there was no way to document which arguments the rake test command would accept. There was also limited ways to parse those arguments.
00:06:14.940 With the new test runner, we can now see the documentation for this command using 'rails test -h.' This documentation reveals that it includes features like rerunning snippets, fast failure handling, backtraces, and displaying differences in output until the end. Earlier, we explored how to run a single test, but we can also run multiple tests simultaneously.
00:06:49.040 For instance, if I want to run tests from two models—User and Post model—I can specify this using line numbers. The test runner will properly execute tests from the specified lines of both models. This new approach eliminates the need to augment rake with new rake tasks to run tests from specific folders; a single command suffices.
00:07:25.050 Furthermore, you can run two tests at a time simply by separating the line numbers with colons. Another new feature is related to backtraces; prior to Rails 5, we had to set environment variables to view full backtraces. Rails did not show the complete stack trace of failed tests and only provided relevant links using its backtrace cleaner.
00:08:00.000 In Rails 5, we can now view complete backtraces by using the '-B' option when running tests to bring out any failures. We can also use the 'fail fast' feature by passing '-F,' which stops the test suite at the first failure rather than running all tests. You will see something like 'interrupted and exiting' in the output.
00:08:41.680 It indicates that only a certain number of assertions were run instead of all tests. A highly desired feature is colored output—we want our test outputs to show in color. This is provided by default in Rails 5 when using the new test runner.
00:09:04.199 This output coloration is achieved through the underlying library, Minitest, which Rails 5 is powered by. Minitest 5 has a plugin architecture enabling the creation of plugins that integrate with its code to customize output. Rails 5 utilizes this capability to provide a custom reporter with colored output and other features.
00:09:29.510 You might wonder whether these tools already exist in other tools like RSpec. Everything I showed you is possible in RSpec, and the point is Rails adopts valuable components into the default stack. Rails ensures that you get functionality right out of the box, without the need for manual configuration.
00:09:56.800 With Rails 5, you receive the new test runner as a default, so if you're starting a new Rails app, you need not configure multiple testing libraries to achieve these features—it comes out of the box.
00:10:21.930 Now that we've discussed running tests, let's move forward to the second part, where we will see how to write tests and what things have changed regarding writing controller tests.
00:10:45.210 With Rails 4, a typical controller test checks whether an article is created properly. I post to the create action, pass some arguments, and verify if I get redirected to the new article page.
00:11:11.880 On the other hand, if we scaffold generate a resource in Rails 5, we see changes. Instead of hitting the create action directly, we now use URLs for requests. We are no longer targeting a specific action but instead working with routes.
00:11:38.780 Furthermore, we now pass params as keyword arguments. Previously, we passed params directly; now, in Rails 5, we use keyword arguments to pass data.
00:12:01.410 Another significant alteration is that the superclass for tests has changed. It is no longer 'ActionController::TestCase' as was the case in Rails 4; it is now 'ActionDispatch::IntegrationTest,' reflecting a shift towards integration tests.
00:12:27.900 This change was prompted by the need to improve testing speed. Integration tests, historically slower, have been optimized to be as fast as functional controller tests due to recent advancements.
00:12:53.060 As a result, Rails has deprecated functional controller tests in favor of integration tests, providing a more accurate representation of how requests function in the real world. When testing controllers using functional tests, we found that they did not utilize the complete middleware stack; they made calls directly to the controller.
00:13:24.400 In reality, requests pass through several layers of middleware before reaching the controller, which integration tests now account for. By default, whenever we generate a resource in Rails 5, integration tests are created instead of functional tests.
00:13:49.510 Don't worry, though—this shift is backward compatible. Existing Rails apps that upgrade to Rails 5 will still run their functional tests as they always did. However, new tests generated in upgraded apps will be integration tests.
00:14:17.150 In Rails 5.1, the 'ActionController::TestCase' might be moved from the core Rails to a new gem for those who want to retain the legacy behavior. Thus, while older test functions remain unaffected, all new tests will now default to integration tests.
00:14:40.590 Let’s explore the implications of this change when writing tests. A typical controller setup requires fetching a user and signing them in. If you use Devise, you might encounter scenarios where user IDs are directly set in the session.
00:15:01.860 In integration tests, we no longer have direct access to sessions; we need to log in users explicitly. For instance, before creating an article, I must first send a POST request to log in the user.
00:15:24.610 With the old method, we would place these actions in a 'before' filter; however, now we need to include this logic within the main test body.
00:15:49.240 The integration test architecture also changes how we handle request headers. Previously, we could alter them by simply accessing the request. In integration tests, headers must be passed explicitly as keyword arguments.
00:16:13.170 Returning to the primary objective of testing our controllers: when testing a controller action, we examine whether the request behaves as intended. We check if an article is created correctly, confirming that a redirect occurs or evaluating response statuses and DOM content.
00:16:38.050 In Rails 4, we checked instance variables and confirmed template rendering using methods like 'assert_template,' but in Rails 5 these practices are no longer recommended.
00:17:05.310 Rails 5 guides suggest focusing on testing the end results instead. You should validate the HTML generated by a controller or the response status code, which can be tested using different tools.
00:17:27.330 Thus, the helpers 'assert assigns' and 'assert_template' have been deprecated for the sake of testing end results, instead of internal implementations.
00:17:50.660 Despite the deprecation of these methods, we still might need to test controller internals, particularly in cases where we require validation of the data being passed to views.
00:18:13.470 In situations such as when developing a Rails engine, verifying the interaction between controllers and views becomes indispensable. If a controller impacts how different templates are rendered, testing may need to extend beyond simple response validation.
00:18:38.760 Consider using the Devise gem as an example: controllers in gems often function separately from the main application, impacting how data gets passed between the two. In such cases, direct interaction with the controller’s internals may be necessary.
00:19:02.790 In general, while controller internals testing has been deprecated, it can still be useful for confirming logic in more complex cases.
00:19:25.460 If you still find it essential to test your controllers with the old methods, you can take advantage of the 'rails-controller-testing' gem. Including this gem in your test suite will keep deprecation warnings at bay.
00:19:50.378 Let's now shift our focus towards API apps. Rails 5 has introduced API-only applications, which can be created using the 'rails new app_name --api' command.
00:20:16.090 Generally, API apps handle JSON and XML data, and our standard model tests remain unchanged. However, changes occur in how we test controllers.
00:20:41.669 To send requests in API apps, especially when testing, particular attention is required. You will need to specify content types, convert attributes to JSON before submitting, and ensure that your request format is compliant.
00:21:10.430 But in Rails 5, we can utilize a mechanism to automatically encode requests to JSON format, reducing the boilerplate code required.
00:21:35.240 When sending requests, you only need to specify the encoder, making Rails handle the intricate steps that were previously necessary.
00:21:54.480 For making assertions on responses, Rails 5 has introduced a response body parsing helper called 'parsed_body,' which figures out if the response received is JSON and parses it accordingly.
00:22:12.480 This new feature streamlines API testing by reducing unnecessary code and improving overall clarity. Moving on, yesterday we had a session about Active Job.
00:22:31.670 The new default sync adapter in Rails 5 is essential for development and test environments, preventing issues where synchronous and asynchronous code lead to false positives in tests.
00:22:47.799 Another enhancement includes enabling random test order by default. Previously, tests were dependent on order, leading to complications when running in parallel.
00:23:05.080 Now, tests are set to run in a randomized order, improving parallel testing capabilities.
00:23:23.650 When it comes to fixtures, Rails 5 introduced several improvements. For example, you can now directly reference JSON files in your test helpers without needing helper methods to access them.
00:23:51.800 In addition, when working with Active Record, if your model class differs from your table name, you can specify the model within the fixture file directly, streamlining your code.
00:24:13.020 This is beneficial for legacy apps where models and tables may not correlate directly. By setting metadata in the fixture files, Rails will correctly load the appropriate model class.
00:24:34.390 These enhancements not only improve your testing experience but also reduce boilerplate code. Overall, testing Rails 5 apps has become a more effective and streamlined process.
00:24:57.990 We can run tests more effectively, focusing on integration testing rather than isolating controller behavior from view behavior.
00:25:21.289 The controller and view interaction is regarded as a black box. You're not recommended to dig into those internals; test what comes out instead.
00:25:44.590 There are gems available if you desire to retain some of the older testing methodologies, so happy testing! Make sure to write quality tests!
00:26:05.789 Before we conclude, how many Star Wars fans do we have here? Today may not be May 4th, but may the force be with you in writing good tests, and feel free to utilize the default Rails stack!
00:26:27.489 I will now hand it over to the next talk given by Justin, who will explain how to use RSpec with Rails 5 effectively.
00:26:51.900 If you're interested in more information about Rails 5, there’s a blog series with around 32 articles covering numerous topics. If you'd like to stay updated on what happens weekly in Rails, subscribe to our newsletter.
00:27:14.800 The RailsConf edition of this newsletter will be available soon, so please subscribe and keep updated. Thank you!