Behavior-Driven Development (BDD)
Steven, Just let_it_be. A Guide To Improve Your RSpec Performance

Summarized using AI

Steven, Just let_it_be. A Guide To Improve Your RSpec Performance

Daniel Susveila • September 21, 2023 • Vilnius, Lithuania

In the presentation titled "Steven, Just Let It Be," Daniel Susveila discusses methods to enhance the performance of RSpec tests, emphasizing their importance in the developer workflow.

The core of the discussion is centered around why optimizing test speed is critical:

- Feedback Loop Efficiency: Fast tests allow for a quicker feedback loop for developers when deploying updates.

- Cost Reduction: Slow tests can lead to increased costs when using services that charge by the minute.

Daniel highlights the prevalent issue of slow test runs, with many developers struggling to keep test suites under ten minutes. To address this, he presents a compelling case for tackling slow tests, using a statistic showing substantial time lost over five years if test runs exceed one second.

He provides a hands-on example involving an RSpec test for a Payment model, emphasizing the best practices for writing tests. Notably, he warns against relying heavily on instance variables due to risks of undetected errors from typos. Instead, he advocates for using RSpec's 'let' method, which helps ensure failures are caught promptly.

Further, Daniel delves into performance considerations, discussing how RSpec resets the database with each context, potentially wasting time. He suggests that reverting to instance variables might enhance performance but raises the concern of state leakage between tests.

To help address slow tests, Daniel introduces RSpec's --profile option to identify slow test examples and groups. Additionally, he discusses a gem called TestProf, which offers tools for managing test factories and optimizing performance, such as tracking factory usage across tests.

One of the pivotal strategies Daniel shares is using the let_it_be method, allowing for efficient reuse of objects across tests, thereby significantly reducing execution times. He demonstrates empirical results where execution times improved dramatically, with some tests dropping to under one millisecond, thus making the overall test suite more reliable and faster.

In conclusion, Daniel imparts that enhancing RSpec performance not only minimizes execution time but also reduces test flakiness, ultimately leading to a more effective testing strategy. He expresses hope that the shared insights will empower developers to optimize their RSpec performance.

Steven, Just let_it_be. A Guide To Improve Your RSpec Performance
Daniel Susveila • September 21, 2023 • Vilnius, Lithuania

EuRuKo 2023

00:00:12 My name is Daniel, and I'll be presenting 'Steven, Just Let It Be.' It's a bit of a clickbait title, but we're going to chat about how to improve the performance of your RSpec tests.
00:00:25 Before we begin, let me introduce myself. I'm 27 years old and from Uruguay, a small country in South America. I'm the full-stack tech lead at Apptrick, and I'm a big fan of Formula One. I noticed some people were watching the free practice, so there are definitely more of us around.
00:00:41 At Apptrick, we focus on app store acquisition. Our platform is driven by data science, helping clients increase their app visibility, gain more downloads, and optimize their ad campaigns.
00:00:58 In today’s talk, we’ll discuss increasing RSpec performance. Euruko this year has been very RSpec-focused, so why not continue with that theme? But first, let's consider why fast testing is important.
00:01:22 While it seems like an easy question to answer, there are many reasons why fast testing is vital. Fast testing is part of our feedback loop as developers. Whenever we're working on features, we run our tests, ideally each time we deploy.
00:01:43 Moreover, those tests run on platforms like GitHub Actions or other services that you might use, and those are often billed by the minute. Thus, having fast tests can make a significant difference financially. Let me ask you all—how many of you have your test suite running in under five minutes?
00:02:07 And how many take between five and ten minutes? Alright, the majority of you are likely running tests longer than that. It’s crucial to address the issue of slow tests, as many developers still face this problem.
00:02:31 Let’s explore a relevant statistic. There's a chart showing how much time you lose if your tests take more than one second. If your tests take over 10 minutes and you run them multiple times a day, you're losing a significant amount of time over a span of five years. It serves as a wake-up call to tackle slow tests.
00:03:05 To illustrate my point, I'll give a practical, albeit simple, example of testing a support class with a couple of Active Record models. In this case, I have a User model with many payments, and each Payment belongs to a User.
00:03:24 The Payment model has an attribute, the amount, which is relevant for our validation class. This validator checks that the purchase is valid if there's no negative amount. A negative payment means it's not valid, and this is what we will test.
00:03:43 There are multiple ways to write tests for this with RSpec. One approach uses a before hook to set up the user and create both a valid and an invalid payment to cover edge cases. For simplicity, let's focus on testing that a payment is invalid if it has a negative amount.
00:04:01 In this test, we're checking that the class returns false for the validation when there’s an associated invalid payment. However, using instance variables in tests is not the best practice because they can lead to errors during testing.
00:04:28 For example, if I make a typo in an instance variable name while associating the payment with the user, the test will pass incorrectly because the invalid payment is associated with a non-existing user. This becomes problematic as it doesn’t alert you to the issue.
00:04:53 To avoid these issues, RSpec introduced the 'let' method, which reduces reliance on instance variables and ensures that any typo or error will cause the test to fail, providing immediate feedback.
00:05:11 Using 'let,' we can write better tests. This way, if a typo occurs, the test alerts us, ensuring that we're aware of any issues before they become problematic.
00:05:37 However, there are performance concerns to consider. Each context in RSpec resets the database, which may not be necessary for tests that do not alter data. In those cases, we can optimize performance by avoiding unnecessary resets.
00:06:01 While reverting back to instance variables for certain tests might help with performance, it comes with the risk of state leaking between tests if you forget to clean up after. This scenario can lead to misleading test results.
00:06:21 So, we’re focusing on slow tests today; performance is one crucial aspect of this. RSpec provides a built-in profiling tool to help diagnose which tests are slow. I had a similar situation in my company with a slow test suite.
00:06:45 To tackle this issue, I researched and found the RSpec `--profile` option. It helps identify the top ten slowest examples and example groups, which is useful for pinpointing what needs to be optimized.
00:07:06 But to dig deeper, I discovered a gem called TestProf that offers many useful features beyond just profiling tests. For example, it can assist with managing test factories more efficiently.
00:07:36 TestProf can help highlight unnecessary factory calls in your tests, showing how many times a factory is used across examples. This insight can help minimize overhead and improve speed.
00:08:00 To demonstrate the performance differences, let's consider an example where we have multiple assertions being performed. If we run tests with the current setup, we may see high factory usage that can lead to slower execution times.
00:08:28 However, employing the `let_it_be` method allows us to define objects only once and reuse them across multiple tests. You will quickly notice that this approach can drastically reduce execution times.
00:08:52 For instance, running a scenario with a hundred assertions can lead to using factories two hundred times in a typical setup, resulting in longer execution times. In contrast, using `let_it_be` allows us to reuse those setups efficiently.
00:09:19 In experiments, we observed execution times drop significantly, with some tests completing in under one millisecond— a considerable improvement from the previous average of almost a second or more.
00:09:45 Beyond just improving speed, optimizing your factory usage can also lead to reduced flakiness in tests, giving you more reliable and trustworthy outcomes. That's why I wanted to share this information with all of you.
00:10:12 Thank you so much for your attention, and I hope you found this discussion on improving RSpec performance valuable!
Explore all talks recorded at EuRuKo 2023
+12