Testing

An Introduction to Test Bench

An Introduction to Test Bench

by Nathan Ladd

In the video titled "An Introduction to Test Bench" presented by Nathan Ladd at wroc_love.rb 2023, the main topic revolves around the Test Bench tool and its connection to Test Driven Development (TDD). Ladd recounts his journey of learning TDD the hard way, leading to the creation of Test Bench, which is a simple yet effective testing tool. Throughout the presentation, Ladd intersperses his personal experiences and insights gained over the years.

Key points discussed include:
- Introduction to Test Bench: Ladd explains that Test Bench is a tool he developed that emphasizes TDD and validation at every step through automation.
- Personal Journey: He shares his background in programming, starting from his learned experience with BASIC and his early career, which was marked by overconfidence in his skills.
- TDD Understanding: Ladd illustrates his misconception about TDD, initially believing it to be just about writing tests but later realizing its core goal is improving the quality of software designs and implementations.
- The Creation of Test Bench: He describes the evolution of Test Bench from a simple idea to a more robust framework over multiple iterations, emphasizing the collaborative efforts with Scott and how it improved their development practices.
- Mistakes and Learning: Ladd discusses his struggles with rushing through implementations, leading to poor design practices and oversight issues which prompted a reevaluation of his coding methods.
- Health Crisis and Perspective Shift: A serious personal crisis, including a significant accident and recovery, prompted Ladd to return to coding with a new mindset, acknowledging the importance of quality over speed in software development.
- Fixtures in Test Bench: The presentation also covers the importance of fixtures in Test Bench, drawing distinctions between various terminologies from the Ruby ecosystem and emphasizing their practical utility in testing.
- Continuous Improvement: Finally, Ladd concludes that software development is an ongoing process, where every line of code is an opportunity for improvement and emphasizes the importance of practicing at a higher level of implementation quality.

This presentation aims to enlighten both new and experienced developers on the significance of TDD in building reliable software and the practical implementation of Test Bench.

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?