00:00:10.320
So, a few more people than I expected, but that's okay. We'll roll with this.
00:00:15.759
If you haven't already done this, head to the GitHub repository at github.com/jesse-speedvac/clean_rspec, fork and clone the repo, and if you can, please complete the welcome survey. This will help me understand who's here and what you're looking for in this presentation.
00:00:29.840
Also, if you want to get bonus points, you can grab a copy of the bingo board located in the getting started section. The idea here is that a workshop of this size is actually quite difficult to navigate, and the real tricky thing is understanding the level of each participant.
00:00:48.399
I might pitch this material way too high or way too low, but hopefully it will be just right. I want everyone to have lots of different options in this context. So if you're an RSpec wizard, grab that bingo board and perhaps this will be more entertaining for you.
00:01:06.000
I understand this is a prime spot; there are many awesome talks happening right now. If you need to get up and go to a different talk, I will take it very personally. I have a camera set up, and if you could tweet or something like, 'Hey, I had a great time at your talk but am heading to a better one,' that would be helpful.
00:01:24.240
Okay, I'm going to switch slides, so if you don't have the repository, make sure that the person sitting next to you has it so you can get it from them. So, I’ll introduce myself now. My name is Jesse Spevack, and I use he/him pronouns. I'm located in Denver, Colorado. These are my twin five-year-olds, Eddie and Audrey.
00:01:52.880
Raise your hand if you are local to Colorado. Wow, a lot of Coloradans here; that's awesome! Currently, I work at a company called Stripe, where we work on the economic infrastructure of the Internet. I wish I could tell you more about Stripe, but this is day two of my third week there, so maybe in a few months, you can ask me about it. I'm very excited about this!
00:02:18.319
Before Stripe, I worked at a company called Ibotta. Are there any Ibotta people here? Okay, cool! I see one person up front. Before I got into coding and the tech world, I came from a non-traditional background. I went through a program called Turing, located in Denver. Raise your hand if you're a Turing alum here.
00:02:40.879
Great! At the time I joined Turing in 2016, there was a proliferation of code schools with a wide array of quality. I would certainly say that Turing is on the high-end quality spectrum. One thing that makes it so high-quality is its mission, which is to bring folks from diverse, non-traditional backgrounds into fulfilling technical careers. I love my Turing community and think it's had a huge impact on this Ruby conference and the Ruby community in general.
00:03:06.560
Before I continue, I also want to take a moment to thank a few people. First, a big thank you to the RubyConf planning team for putting this event on. If you see anyone in a red shirt who is volunteering, please thank them! I also want to express my gratitude to the folks at Confreaks who are handling the video and audio. I appreciate your hard work, and finally, thank you all for coming and using this prime time at RubyConf to explore RSpec with me.
00:03:44.159
Now, raise your hand if you've ever heard of the Gilded Rose kata or a code kata. Let me ask a more general question: raise your hand if you have heard of a code kata before. Great! I'm supposed to remind you—don't be afraid of the mics. If you need to ask a question, please use the mic. So, the idea of a code kata is an exercise designed to learn a specific coding principle. There's a famous code kata called the Gilded Rose kata, which is noted for a couple of reasons.
00:04:14.400
For one, it has an origin story connected to World of Warcraft. Raise your hand if you've ever played World of Warcraft. Some folks are familiar with this game, which debuted in 2004 and became quite popular. Although I was never addicted to it, this kata incorporates concepts from a specific element of World of Warcraft called Gilded Rose. It has gained fame largely due to a talk given by Sandy Metz a few years ago called 'All the Little Things.'
00:04:51.840
Raise your hand if you've seen this talk from 2014. Excellent! If you plan to leave this talk early, the best reason to leave would be to watch this talk, which is available on YouTube on the Confreaks playlist for RailsConf 2014. In that talk, Sandy explores the Gilded Rose kata to teach some intriguing object-oriented design practices. I thought it would be interesting to use this code that I believe some might be familiar with to discuss good testing design and, specifically, effective RSpec practices.
00:05:38.960
Without further ado, I hope we accomplish a few objectives during this workshop. First, I want you all to leave with a clear understanding of the purpose and benefits of testing. Secondly, I aim to clarify what we mean by 'the object under test.' I want to convey how testing can be utilized to document code functionality effectively. Additionally, everyone should grasp the three-phase test pattern and understand that we should strive to write our tests in a manner that optimizes for readability.
00:06:13.280
Finally, I believe we should be cautious regarding the use of test doubles, which we will discuss further. Now, just a little disclaimer as a prologue to the workshop format: I used to be a teacher, and this format is inspired by that experience. While I've never taught in a classroom that looks quite like this, the idea is that I will demonstrate something up here, we’re going to do something together, and then you'll get a chance to try it on your own. This pattern will be followed throughout the workshop.
00:06:44.160
Perhaps this structure will work for you because the 'you do' portion will be just at the right level. However, it might also be too easy or too difficult, in which case, the documentation and the README closely follows the directions I will be going through in the workshop. If you feel the need to speed ahead, you are welcome to do so, and if we start moving ahead as a group, you have that documentation available to you.
00:07:01.199
Now, I would like to engage in some audience participation. I would appreciate someone brave enough to join me in discussing the first question: Why do we test? As a community, why should we all be present here right now?
00:07:31.680
To validate that our code does what the business requires. To catch bugs before QA. To prevent breaking my own code. To document the code. I think that covers a lot of the rationale behind testing. While some folks might offer different reasoning for their testing practices, I hope the things I've presented resonate with those of you who share similar thoughts. It's important to distinguish between two types of tests: unit tests and integration tests. A unit test targets a specific small portion of business logic, like a single function or class, whereas an integration test assesses the interaction between multiple components.
00:09:13.360
As a practical example, think of a unit test as testing a specific function, perhaps like counting participants in a workshop. An integration test, on the other hand, could represent the larger interaction of multiple systems. It's essential to mention that there are distinguished methodologies around units and integration tests. Another concept to introduce is the distinction between query methods and command methods. A query method returns something and changes nothing, while a command method doesn't return anything but does change some state.
00:10:09.520
I'll show you a method, and I want you to decide with the person next to you whether it's a query method or a command method. Talk it over for 30 seconds.
00:10:43.520
Okay, who wants to share their thoughts with the group? Yes, that's a good observation—it's a query method. Has anyone else thought about this?
00:12:10.960
Indeed, I think we've all been in situations where a method's name doesn't clearly indicate its behavior. Thus, distinguishing a query from a command can be tricky. When you have a query method, you want to test the return value. For instance, if I want to validate the workshop count, I should affirm that calling workshop count returns the value I expect.
00:13:00.400
Conversely, when dealing with a command method, you're testing the state change. For example, with an enroll operation, I won't check for a return value but rather test to ensure that the participant count has increased as expected.
00:13:45.440
Sandy Metz provided a helpful diagram for understanding this distinction, and we will discuss it further, including the topic of test doubles. Now, let's dive into the first workshop activity. The goal is to understand the object under test better. Often, when asked to implement new code or comprehend existing functionality, my first instinct is to look at the test file.
00:14:28.160
There's a prevalent belief that tests serve as a form of documentation for code. However, I find that it’s quite rare for me to look at an RSpec file and obtain significant clarity about expected outcomes. How do you all feel? Raise your hand if you usually don’t gain clarity from opening an RSpec file.
00:15:24.080
Many of us share a common struggle. At work, we sometimes host Turing graduates who are new to the tech world. I often find the most time-consuming part during their onboarding is trying to explain our spec files.
00:16:12.400
In these instances, I realize we can optimize the way we write tests to ensure clarity for ourselves and our new teammates. Now let me show you some code that's valid in RSpec. There's a keyword called 'subject.' Many of you are familiar with it, so for brevity, I won't go into detail.
00:16:40.640
When you don't explicitly define what 'subject' refers to, it can create confusion, especially for beginners or those transitioning from other languages or testing frameworks. How can we improve this?
00:17:27.680
We can make it explicit. The two examples I'm sharing are functionally identical, but in the improved one, I clarify what the subject is, allowing anyone reading it to quickly understand that we're focusing on a new Workshop object. In my RSpec practices, I recommend calling out the subject right up front.
00:18:09.840
Additionally, when I write RSpec, I name my subject and reference it in subsequent tests for clarity's sake. Now, I want you all to participate: open the repo I asked you to clone, and on line seven of the Gilded Rose spec, there is some poorly written RSpec meant to demonstrate how to improve clarity. Take a few minutes to see if you can reformat it.
00:19:07.640
If you have time, commit and push your changes, and then we can start discussing your code without putting anyone on the spot. Let's take about three minutes for this activity.
00:19:39.040
Another possible extension activity for those who are feeling confident already is to start applying the Gilded Rose kata. If you achieve bingo with your board, don't hesitate to wave your hand, and I will congratulate you. Just a reminder: if this feels too slow, feel free to dive into the bingo board or jump ahead.
00:20:18.080
Did anyone make progress applying the workshop example to the Gilded Rose example? Yes? Awesome! Essentially, it simplifies matters by removing the 'subject' designation and calling it out upfront, allowing us to focus on 'gilded rose' directly.
00:21:02.240
Is there a question or comment? Do you ever use 'describe class' instead of calling out the regular name? I actually appreciate using the 'describe class' keyword as it allows me to rename a class at any point without going through all my tests.
00:21:33.440
To recap, moving the subject out of individual tests and upfront where it can be easily referenced is beneficial. Instead of a generic subject keyword, a clearer name like 'gilded rose' makes the purpose of the tests more transparent.
00:22:26.640
In line eleven of your Gilded Rose spec, there's a test you might consider cleaning up with 'describe', 'it', and 'context' blocks. I encourage you to explore breaking it down for clarity and let's see what you come up with. You have five minutes for this activity.
00:23:16.240
Time’s up! Who wants to share how they incorporated 'describe', 'it', and 'context' into the test on line eleven? That’s a fantastic approach—segmenting based on specific conditions helps clarify the test's intent.
00:23:44.000
One effective method to improve readability is ensuring every test cell is associated with a clear, specific condition or state. I appreciate how you all are dissecting the tests according to different conditions, as this strengthens test documentation. Let's now pull up line eleven and review how we can make further improvements to our tests in alignment with the three-phase test pattern.
00:24:56.000
For clarity, the three-phase test pattern includes: 1) Arrange—setting up only what's necessary. 2) Act—performing the action under test. 3) Assert—checking the expected outcome. If a test starts 50 lines long, it complicates understanding what's actually being validated.
00:25:53.280
Let’s refine a long test into these phases. We can avoid unnecessary redundancy by focusing specifically on the actions and outcomes we want to validate within each test context. Streamlining tests can enhance readability and assist both current and future developers, making it clearer what is being validated.
00:26:51.440
A key aspect of testing is ensuring that we have meaningful assertions in place to avoid unnecessary redundancy. To reiterate, we're aiming to establish a clear three-phase pattern: setting things up, executing the action, and then asserting the expected state change.
00:27:24.400
Let's jump back to line eleven of our Gilded Rose spec file—try to refactor it to align with the three-phase test pattern we've discussed. Take a few minutes to think through how you could set up, act, and assert effectively.
00:28:20.640
What changes did you implement? Does anyone want to share their approach to restructuring the test? That's a great change to eliminate redundant assertions and emphasize clarity in outcomes.
00:28:45.760
It sounds like a productive discussion! Establishing contexts for various conditions provides transparency and sheds light on the underlying behavior of the code. Well done!
00:29:32.480
Moving on to the next segment, we're going to talk about shared examples. Please raise your hand if you've seen this feature used before.
00:30:10.480
Here’s how it works: you define shared behavior with an example and can reuse the shared example numerous times throughout your tests. This can create efficiency in writing tests. However, this approach can produce a lack of clarity for anyone reading the tests later. To ensure the readability of the tests, refactoring them to express the intent clearly and directly may be the better solution.
00:31:06.160
Instead of writing shared examples that may obscure the functionality being tested, you should document your expectations explicitly tailored to each test narrative. This will clarify not only the purpose but also the conditions being evaluated within each test.
00:31:43.840
Let's move on to discussing test doubles now. Raise your hand if you heard any enlightening concepts from earlier talks today related to test doubles or mocking. It's crucial to leverage test doubles judiciously in your testing strategy.
00:32:24.920
We use test doubles to create controlled environments where we can validate interactions, particularly when testing units in isolation. It's important to keep in mind not to overuse them, as they can lead to brittle tests and could obscure the intent of what you're aiming to validate.
00:33:29.200
Let's revisit the concept of discovering what your code should do through tests. Using test doubles effectively allows us to focus on designing tests that reflect the intended interactions and behaviors of the units without being overly prescriptive about implementation details.
00:34:12.320
In testing practices, we should avoid stubbing behaviors on the very object we are testing. For instance, if I need to validate whether a workshop sends notifications appropriately, I shouldn't stub that behavior on the workshop itself. Instead, consider structuring your tests around external dependencies that offer these services.
00:34:59.120
As we deliberate on our testing strategies, ensure you focus on using test doubles responsibly, balancing mock usages with actual behavior verification. Your test doubles should serve to illuminate the paths necessary to validate behaviors rather than overshadow them.
00:35:47.600
As we conclude, I sincerely thank all of you for participating in this workshop. We will wrap things up shortly, but if you are interested in further discussions related to testing practices or any other concepts we explored today, I will be around for the next 30 minutes. Don't hesitate to reach out, whether it's about the Gilded Rose kata or any other aspects of Ruby testing.
00:36:38.800
Once again, thank you all for being present. I genuinely appreciate the engaging conversations and collaboration. Let's continue to learn from each other and enhance our understanding of testing in Ruby!