wroc_love.rb 2023

An Introduction to Test Bench

wroc_love.rb 2023

00:00:03.920 Thank you.
00:00:10.920 Are we up here? There we go. All right, it's good to be back.
00:00:17.220 I'm in Roswell again. I think last time I was here it was 2018, in March.
00:00:24.300 March in Poland can still be frigid; it was incredibly cold and reminded me of growing up in Alaska.
00:00:29.580 And I'm not joking.
00:00:36.000 The first thing I’d like to get out of the way is that this isn’t a sales pitch.
00:00:42.180 I am the author of Test Bench, and yes, I believe it’s worth checking out.
00:00:47.879 I will be introducing it to you, but it's a very simple tool and its value may not be immediately apparent.
00:00:54.780 The code snippet I'm going to show you is trivial enough to give you the gist of it, and it won't look much different from other frameworks.
00:01:02.280 I’m going to interleave both the history of Test Bench and my own experience developing it in this presentation.
00:01:09.299 If I wanted to title the story of Test Bench, I might start with how I learned TDD the hard way.
00:01:14.820 But 'hard' doesn't even begin to describe it.
00:01:21.119 A better way to phrase it would be how I learned TDD in the most excruciating, agonizing, painful way imaginable.
00:01:26.820 Some of you who have known me for a few years might have an idea of where this is headed.
00:01:33.380 Test Bench and TDD are inextricable.
00:01:40.140 What makes Test Bench unique is that it was produced through the process of Test Driven Development.
00:01:47.700 Remember, XUnit frameworks emerged even before I learned to code.
00:01:53.579 Tools for test automation predate TDD.
00:02:00.180 When I say that what makes Test Bench unique is that it was produced by TDD, I mean something novel here.
00:02:07.439 It doesn't mean that I wrote tests first, although I did; it also doesn't mean I did Red-Green-Refactor, although that happened too.
00:02:13.800 It’s about validating every step of the way through the lens of our test automation.
00:02:19.860 A little bit about me: I’ll dive into some details about myself here, as it's part of communicating a central point.
00:02:25.379 I am one of those '90s kids who grew up with a computer and BASIC.
00:02:31.560 If anyone recalls this time, we’d get magazines with little video games whose source code was printed in the back.
00:02:37.160 I learned to program by transcribing line-for-line the code from the magazines into QBasic.
00:02:42.840 That's kind of how I got started.
00:02:48.239 As a side note, I also started teaching myself guitar, and I was pretty good for a self-taught amateur.
00:02:55.980 However, I really couldn't keep time; if I'd gotten a teacher, they would have put me on a metronome.
00:03:03.959 This consistent theme reflects a significant limitation of mine: my temptation to rush.
00:03:09.739 Even right now, I feel like just blasting through this presentation.
00:03:18.780 But yes, I have to take a beat.
00:03:26.099 Motivating myself for school was tough; if it wasn't fun, I wasn't really going to do it.
00:03:32.340 I was really more into video games and fantasy novels.
00:03:39.060 I wanted to be a hard worker, but it just didn’t happen.
00:03:46.680 I dropped out of university but soon found a job in programming.
00:03:52.200 Actually, I was quite good at that job, though I didn’t realize it at the time.
00:03:59.519 This isn't an endorsement of my abilities; I stumbled into a very favorable working condition by luck.
00:04:07.140 Working as a programmer suited my strengths and largely ignored my weaknesses and limitations.
00:04:13.860 I was good at getting code to work, and that's all I really needed to find gainful employment.
00:04:20.100 I enjoyed early success, and unfortunately, that led to a pretty big ego.
00:04:26.340 I grew overconfident, believing I understood everything about programming.
00:04:31.500 I started out developing low-level device drivers for Linux, and I didn't learn web development until much later.
00:04:38.160 Five years into my career, around 2008 or 2009, I still had no idea that I was consistently rushing my implementations.
00:04:45.960 Not only was my work rushed, but my understanding of fundamental software concepts was makeshift at best.
00:04:52.080 By the time Scott and I started working together in 2015, if you'd asked me what TDD was, I'd have assured you I knew.
00:04:59.759 I was well versed in it; it was a popular topic, after all.
00:05:06.960 You write your tests, make the tests pass, and then refactor the code until it’s better.
00:05:12.900 That's the gist of it, right? The problem is, that’s not really what TDD is about.
00:05:20.100 That's just a routine procedure; it doesn't state the objective.
00:05:26.520 I'll go into this more, but suffice it to say I simply didn't understand the underlying premise of TDD or software design.
00:05:33.240 Not nearly as much as I thought I did, at least.
00:05:40.140 When Scott and I started working together in 2015, I think working with him was a wonderful experience.
00:05:46.680 It was particularly valuable if you're unknowingly harboring misunderstandings about fundamental software principles.
00:05:53.280 We were working on a project together and had some back-and-forth about choosing our test framework.
00:06:01.320 At that time, I was a devoted MiniTest user, and I didn’t really understand the excitement around RSpec.
00:06:07.200 I was used to testing with MiniTest, and at that time, RSpec had transitioned its syntax.
00:06:14.160 Scott pointed out the differences between 'must equal' and 'should equal', stating these aren't semantically equivalent.
00:06:19.860 I accepted his point, even if I didn't fully grasp its importance at the time.
00:06:26.400 This dissatisfaction pushed us to consider how our test framework should operate.
00:06:32.700 Wouldn't it just be nice if our test files were simply scripts?
00:06:38.640 Ultimately, automated tests are just procedures. Why not write procedural code?
00:06:45.960 We want to run them with Ruby without needing elaborate assertions.
00:06:52.560 For us, logging outputs every value that goes through our objects was sufficient.
00:06:59.880 We didn't require anything particularly elaborate.
00:07:07.140 We just needed the ability to establish contexts and write a little prose.
00:07:14.400 The tests would include assert and refute methods within each block.
00:07:21.240 There’s nothing complicated about this, and as a result, this was fairly simple to script.
00:07:28.560 This is what Test Bench looks like—very simple.
00:07:38.640 The output shows the context and tests in your test, reflecting the structure.
00:07:45.000 I sketched an initial bootstrap implementation. It's essentially assert, refute, context, and tests.
00:07:50.580 This was released on March 31st, 2016.
00:07:57.420 After that, we transitioned all our projects at work over to Test Bench from MiniTest.
00:08:05.520 Even in its limited early form, our experience with Test Bench was equivalent to what we had with MiniTest.
00:08:12.420 Several years later, in late 2018, I decided to undertake a rewrite of Test Bench.
00:08:19.500 This culminated in its official V1 release and necessitated the use of TDD.
00:08:27.600 I realized TDD was fundamentally about improving the quality of designs and implementations.
00:08:35.040 However, I found that my implementations at work were consistently not good enough.
00:08:42.240 We would go back and forth about glaring mistakes that I often missed until they were pointed out.
00:08:48.840 The common cause was rushing; the first 20 percent of my efforts were enjoyable.
00:08:55.140 I would declare the work done once I got it working, without doing the refactoring step of Red-Green-Refactor.
00:09:02.640 Instead, I merely rearranged code in my editor, thinking it was refactoring.
00:09:09.420 This wasn’t fun; meticulously spotting mistakes and correcting them was not my strong suit.
00:09:16.680 A common theme for me was running afoul of design principles, and I grew tired of brutal code reviews.
00:09:23.280 I wanted to learn how to get implementations right the first time.
00:09:30.480 To achieve this, I needed to stop making so many mistakes.
00:09:37.640 Scott and I often discussed how a mistake isn't merely a defect; rather, it obstructs our understanding.
00:09:45.300 We strive for implementations with no obstructions to understanding.
00:09:52.560 I established some ground rules for myself. I would devote my spare time to re-implementing Test Bench.
00:09:59.280 I committed to a process that would yield better results and help me practice working at a higher level of implementation quality.
00:10:05.760 The motivation behind this was to eliminate design mistakes caused by rushing.
00:10:11.520 I also decided to apply youthful objects in my own project.
00:10:19.380 The previous incarnation of Test Bench was written in a way I had learned before collaborating with Scott.
00:10:26.880 I set uncompromising design principles for myself this time.
00:10:33.480 If I encountered a mistake or discovered one along the way, I would correct it immediately.
00:10:40.620 When working with numerous objects, one mistake could ripple through the implementation.
00:10:47.520 I agreed to correct any fallout caused by the original mistake.
00:10:55.080 If I ever felt out of control of the implementation, I would start over completely.
00:11:02.760 To prevent total loss, I would set up checkpoints—known good positions in the implementation.
00:11:09.840 I had to trust the process.
00:11:16.440 What I’m describing may come off as obsessive-compulsive behavior, and I don’t recommend it.
00:11:22.880 However, if we trust design principles and follow a meticulous process, we could avoid the pitfalls I faced.
00:11:30.000 Constantly challenging ourselves to understand our work better is crucial.
00:11:36.300 Initially, I was wiping the project clean frequently, leading to discouragement.
00:11:42.960 So, I started using Git to create checkpoints, reworking my approach.
00:11:50.520 I structured my Git history so that the earliest commits in my branch were crucial inner-core classes.
00:11:57.480 This allowed me to leverage interactive rebasing.
00:12:05.160 If I found a mistake in one of those core classes, I could backtrack.
00:12:12.720 I could then run tests to ensure each new class I introduced maintained a known good state.
00:12:18.780 The process forced me to see the value of encapsulation more clearly.
00:12:25.320 It made me realize the importance of providing everything needed for downstream users of a class.
00:12:31.680 I realized I was in over my head when I diverted focus from Test Bench to develop a framework for terminal output styling.
00:12:38.040 Unfortunately, my journals from that period are a mess.
00:12:45.000 This Test Bench rewrite had become an overwhelming second and third job for me.
00:12:52.440 Substance abuse was also an issue at that time.
00:12:59.460 This culminated in a bizarre turn of events.
00:13:05.640 If you wish to avoid gruesome details, now would be the time to look away.
00:13:12.360 I experienced a complete nervous breakdown and fell off a balcony four stories down.
00:13:20.400 I sustained seven broken ribs, a broken sternum, a broken clavicle, a broken femur, and a broken face.
00:13:27.300 My injuries also included a broken nose and a Lefort 3 fracture.
00:13:34.200 During this time, Ethan Garofalo, who presented at WrocLove, coordinated a wonderful video for me.
00:13:40.320 This support was incredibly motivating, and I genuinely appreciate it.
00:13:47.760 After being discharged from the hospital, I returned to Test Bench with a fresh perspective.
00:13:54.540 Surprisingly, I found that the implementation quality was decent.
00:14:01.140 I reflected on the code I’d written six months prior and considered it acceptable.
00:14:06.840 From an industrial perspective, nobody could justify my extremely slow pace of work.
00:14:13.740 However, with my approach and patience, I had improved my ability to tackle new problems.
00:14:20.640 I credit Scott for teaching me that in everything we do, we're either improving or settling for less.
00:14:26.880 The biggest opportunity for improvement lies in practicing at a higher level.
00:14:34.560 Slowing down and approaching every line of code as an opportunity for improvement is essential.
00:14:40.560 That's it.
00:14:49.680 Any questions?
00:14:53.760 You kept mentioning fixtures or specific terminology that deserve clarification.
00:15:01.920 If you say 'fixture' from the Test Bench perspective and you're a Rails developer, you might not draw the same conclusion.
00:15:07.560 Would you clarify what we mean by that?
00:15:13.400 Certainly, I skipped the presenter notes meant to address this.
00:15:20.520 Earlier in the presentation, I demonstrated that you could integrate the Test Bench DSL into a class.
00:15:26.880 This revealed a new capability we had for implementing test objects.
00:15:32.040 The term 'fixture' arose from a lengthy debate about what to name this module.
00:15:41.040 Ultimately, despite its coincidence with Rails, we maintained the term 'fixture.'
00:15:47.280 Web applications often require specific data to be loaded into the database, which has altered the term's meaning.
00:15:54.840 Historically, 'fixture' referred to a device for testing something. It's behavioral, not data-driven.
00:16:07.320 Back in 1989, Kent Beck introduced the concept while discussing XUnit in Smalltalk.
00:16:13.560 Fixture has always been about the testing aspect; however, Rails adopted the term for data.
00:16:19.680 This has caused some ambiguity that we’re introducing with Test Bench.
00:16:25.920 All I can say is if you find a better term than 'Test Bench fixture', I'm all ears.
00:16:31.920 A helpful analogy is comparing a fixture to a light fixture.
00:16:38.040 A light fixture holds and powers a lightbulb; similarly, a test object holds the methods that test other objects.
00:16:44.640 This is the story behind the Test Bench fixture.
00:16:52.560 Thank you for calling that out, Scott.
00:16:59.520 When writing tests, I often want to assert not merely literal outputs but some properties of outputs.
00:17:06.360 For example, I may want to assert that an array contains a hash with a specific key.
00:17:12.840 I prefer RSpec’s composable style in such cases.
00:17:20.040 How do we implement such tests with the relatively small API of Test Bench?
00:17:25.740 That's a great question! In a current project, we integrate with third-party APIs.
00:17:33.120 We normally regard objects emitting large sets of primitive data as suspicious.
00:17:40.080 The 'tell, don't ask' principle guides this view; we don’t want to receive excessive data unless absolutely necessary.
00:17:47.520 In our current project, we work with bizarre and complex data structures from APIs.
00:17:54.180 We’ve implemented fixtures allowing us to assert elements and other properties specifically.
00:18:00.780 We can compare individual attributes of a JSON response, for example.
00:18:08.040 Using Nokogiri and CSS selectors, we can assert specific XML elements.
00:18:14.700 This direction aims to specialize our fixtures within Test Bench.
00:18:22.080 Instead of relying on generic outputs, we can build tailored assertions for our needs.
00:18:29.280 Fixtures can significantly simplify the process.
00:18:36.900 To conclude, fixtures are to Test Bench what matchers are to RSpec.
00:18:43.320 Despite some unfortunate history in Ruby, we aim for clarity in our testing approach.
00:18:50.880 A fixture is simply a class, just like any other class.
00:18:57.600 By including the Test Bench module, we gain access to all features.
00:19:03.840 It can perform the necessary assertions and organize our tests neatly.
00:19:11.160 In essence, we can structure our tests in a nested fashion that simplifies assertions.
00:19:17.520 Our focus should be on fundamental principles rather than the complexity of the framework.
00:19:23.880 With Test Bench, the aim is to eliminate needless complications and focus right on what you need.
00:19:30.240 This way, we can emphasize simplicity in our designs.
00:19:37.320 As we develop, we must always consider best practices and how we can systematically enhance our code.
00:19:44.520 Despite years of development, there is still much room for improvement.
00:19:50.520 Indeed, software is never 'done'; it continually awaits future enhancements.
00:19:57.920 The practice of refining software never ends, which is an important lesson from my experience.
00:20:05.400 Perfect practice, as my high school soccer coach used to say, is key.
00:20:12.480 Practice alone makes permanent, not perfect.
00:20:20.040 The biggest takeaway from developing Test Bench is that everything you do enhances your skills.
00:20:26.000 When we tackle coding challenges, we should do our best to improve.
00:20:32.760 It is essential to view every line of code as an opportunity for improvement.
00:20:39.420 That's it.
00:20:51.000 Thank you, Nathan.
00:20:56.760 Any questions?