Talks
The Other Junk Drawer: My Tests are a Mess
Summarized using AI

The Other Junk Drawer: My Tests are a Mess

by Christopher Sexton

In this talk titled "The Other Junk Drawer: My Tests are a Mess," Christopher Sexton presents his reflections on the importance of maintaining a clean and organized test suite in software development. Drawing from his experiences, Christopher emphasizes that while testing is essential for reducing bugs and facilitating design choices, the management of tests introduces its own challenges. He proposes a philosophical approach to testing that prioritizes simplicity, suggesting that well-structured tests are easier to maintain and understand over time.

Key Points Discussed:
- Reality of Disorganization: Christopher admits that his tests have become disorganized, likening them to a junk drawer, and acknowledges the ease with which test suites can become messy.
- Philosophical Perspective: Encouraging developers to reflect on the purpose of tests, he discusses the need for clear goals that align with domain priorities and business needs.
- Simplicity vs. Complexity: Christopher highlights the difference between complexity (the number of components) and complication (difficulty in understanding), advocating for systems that are complex yet comprehensible.
- Three Key Concepts:
- Priority: He illustrates the importance of prioritizing test organization alongside feature development by sharing a personal anecdote about his son's messy room.
- Intent: He uses a story about his daughter to stress the importance of understanding underlying problems that lead to testing chaos.
- Habits: Christopher underscores the necessity of developing good testing habits consistently, even in seemingly simple cases.
- Test Levels: He discusses the significance of both high-level acceptance tests, which provide a broad overview of project functionality, and unit tests that focus on smaller code components.
- Organizational Strategy: Christopher advocates for a mirrored directory structure in test organization, ensuring that test files correlate neatly with application files.
- Discussion and Collaboration: He emphasizes the need for collaboration with team members to identify essential tests and to ensure that testing efforts are aligned with project goals.
- Red-Green-Refactor Cycle: Christopher recommends applying this cycle effectively while managing test setups, particularly in relation to acceptance tests.

In conclusion, the talk reinforces that testing need not be burdensome; with intentional design and a focus on simplicity, developers can create effective tests that enhance code readability and maintainability. The key takeaway is that a thoughtful approach to testing can transform a messy test suite into a valuable asset for ongoing development.

00:00:25.640 Testing is a topic that many Rubyists can get really into. When I first started in the community, it felt like we would go to conferences and all we talked about was testing. Now, I've been trying to engage in proper Test-Driven Development (TDD) and spend time doing things the right way. I’ve discovered a few things about my tests; in fact, I feel like they've become the other junk drawer.
00:00:36.200 Hi, I'm Chris. I work at a little company called Radio Networks. We do a lot of work with mobile proximity and related technologies. I've brought some swag, so if you're interested in things like iBeacons, come and find me. I have some goodies to give away, which is my justification to the CEO for why they needed to pay for me to fly out to Salt Lake City. But the confession I must make is that my tests are, in fact, a mess.
00:00:58.760 I feel like it's very easy for tests to become disorganized, and I've been spending a fair amount of time working with a couple of other engineers who care deeply about having a solid test suite we can trust. Now we want to know if TDD is really the solution. I want maintainable code, and I try really hard to achieve that.
00:01:07.520 So, I think we need to take a step back from the code and concentrate on what we really want from our test suites. More philosophically, where do we want our code to go? We need to figure out what's important to our domain and what the business actually cares about.
00:01:16.560 We need direction and a clear goal. First and foremost, I believe most people here probably agree that we need to test properly. I'd be surprised if anyone would argue against the necessity of unit tests or any form of testing. We know that testing can help reduce bugs, drive design decisions, and make it easier to change our code. However, we also have to manage our tests. This adds another layer of complexity that we haven't necessarily discussed yet.
00:01:43.040 My real goal in considering all of this and reflecting philosophically about testing philosophies is to keep things simple. My intention is not to write beautiful or elegant code, but rather code that I can understand six months later. I've been unsuccessful in the past where I've written something and returned to it later, completely puzzled. When I looked at the edit history, I saw it was just my name listed on every line. Simplicity is hard, but it's essential for our work.
00:02:03.920 While I think everyone intellectually agrees that simplicity is crucial, I’m not convinced that everyone truly believes it. I often hear people rave about intricate systems that are so complicated that they can’t even understand what they do. This needs to change. It is common for us to look at something complex and admire how it fits together, but it is easy to build something complicated. Everything is in your head, and you know all the moving parts, but this is not a good thing.
00:02:26.600 We should strive to create systems we can comprehend more easily. The downside of complicating things is that when you want to add something, it often means tacking it on top of an already complex setup. This leads us to a situation where things become even more convoluted.
00:02:41.200 There’s a difference between complicated and complex. Complexity refers to the number of components in a system, while complicated indicates a high level of difficulty in understanding those components. Unlike complexity, which may have fewer moving parts, complexity suggests those parts are intricately interwoven, making it challenging to understand what's going on. We should aim for a system that has various components that we can reason about and understand.
00:03:32.640 Let me give you three key concepts to consider while writing tests. The first is priority. If you know me, you know I have kids. One day, I walked into my son’s room to find it in complete chaos: he had been pulling things out and dumping his dresser into a disaster zone. In the midst of this, he looked up at me and said, 'I haven't done my homework. Isn't it more important to do that?' While I agreed that homework is indeed essential, I explained that cleaning his room is also a priority.
00:03:47.520 This situation resembles what often happens in priority meetings when engineers mention cleaning up the test suite. There's always a push to prioritize building new features instead of addressing messy tests. The second thing to consider is intent. This reminds me of my daughter during her potty training phase. She was trying to do the right thing by getting toilet paper and ended up pulling the entire roll and becoming upset. This is a call to step back and assess how we got into the situation where we are going wrong because sometimes we find ourselves in a mess without realizing it.
00:04:05.640 So, we must pay attention and know when to stop. The third principle is about habits. When driving alone, it’s often easy to forget to use your turn signal, but developing that habit is essential. This principle applies when writing tests too. Even for simple functions, it's vital to maintain good practices always—even when things seem straightforward. Always strive for cleanliness and organization in your code and test suite.
00:04:52.520 We need to figure out what we need to test. A lot of complexity comes from trying to handle all potential cases and worrying about things that don’t matter to the business. There are critical aspects we need to focus on when writing tests, like making sure that the ventilation shaft is properly covered; there’s a difference between that and obsessing over the plating on the side of a small planet. It is vital to distinguish between what is essential to test and what is not.
00:05:14.160 High-level acceptance tests are something I really value. They serve not only as regressions but also as a good way to steer the design from a broader perspective of what we genuinely want. This way, we build a working system that truly meets our needs. These tests will keep the business and your leadership happy, providing you with proof that the entire project still operates correctly.
00:05:36.080 Unit tests, on the other hand, focus on the smaller components. Everyone here likely knows what they are: they're the granular pieces of your code that should be broken down. In this case, complexity is more manageable than complication. If you're interested in refining your unit testing skills, check out Sandy's talks on testing methods. I've watched them multiple times as they offer solid basics on how to handle outgoing messages, queries versus commands, and how to break things down effectively.
00:06:21.600 The challenge is determining how much to test with all the content in between. Myself, I tend to avoid extensive testing. I often write fairly minimal controller tests because as soon as anything starts to become complicated, I prefer to pull it out and place it into a separate model where it can be managed more efficiently. In essence, I want the parts that wire the system together to be covered by the acceptance tests rather than repeat that with unit tests.
00:07:02.520 However, we must pay attention to the domain we’re working in to ensure that we're covering what truly matters. Communicate with your colleagues about the project’s needs. Determine what aspects are crucial for the project versus what might be peripheral. Sometimes, you may only need a solid unit test suite and a few happy-path tests.
00:07:36.440 As we explore this philosophical aspect, it's essential to address the parts each test entails. The basic structure of a test is often referred to as Arrange, Act, Assert or, similarly, Arrange, Act, Assert. So, you want to arrange all necessary inputs, call the method you’re testing, and confirm that the output matches your expectations. Pay attention to how these parts interact and assist in making sure everything works.
00:08:09.280 Before allowing things to spiral out of control, everything needs to stay organized. Having a tidy setup, particularly in complex tests, prevents chaos. My wife enjoys cooking and takes classes where there’s an emphasis on organization. In a professional kitchen, chefs ensure everything is meticulously placed, which reinforces the importance of maintaining order in your code and tests.
00:08:40.600 As you create your test suite, avoid creating a disorderly mass where tests for different components or models intermingle. If things get messy initially, it’s easily overlooked, but you need to always be mindful. I advocate for a mirrored directory structure: if your `lib` directory has specific folders (like models), your `spec` or `test` directory should reflect the same structure.
00:09:07.920 This will make it easier to locate the files associated with the code when you revisit them later, especially months down the line when you might forget details about the project. While discussing this before, we explored where to put acceptance tests. Typically, I’d suggest creating a folder called acceptance for this purpose, consistent with the terminology we use in conversations around testing.
00:09:54.920 Ultimately, it's important to step back and evaluate which elements matter in your domain. Determine how components can be grouped logically, and manage their states effectively. Understand how objects should be organized into relevant models or libraries. Real-world systems are often inherently complicated and messy, and it takes strategic thought to align your code accordingly while avoiding leaky abstractions.
00:10:25.760 Managing the setup phase of your tests is crucial. As you’re building out your tests, always keep an eye on how you arrange the setup. I’ve encountered instances where during TDD, the resulting code becomes quite clean simply by the nature of the process. But as soon as scenarios involve multiple factors—like adding an admin user or additional attributes—this can lead to a tangled mess if not careful.
00:11:03.840 One solution I propose is flattening setups. It's acceptable to have certain setups become more complicated, as long as they make sense within the domain and are coherent. For instance, consider an e-commerce platform where orders, admin users, and billing information tie into one coherent structure. It's about discussing and figuring out the significant cases with your team during development.
00:11:35.760 Once you've identified key setups, apply the red-green-refactor cycle optimally. This means prioritizing setups relevant to acceptance tests rather than complicating test assertions. Always try to keep the systems clean and readable. If the complexity of the tests stifles understanding, it’s time to review the setups and see how you can simplify them for future reference.
00:12:01.760 As you consider how to manage your test suite, remember to take a step back. Looking at the domain, understanding the app, and determining what actually matters is vital. Have discussions focusing on what is essential rather than fixating on code. Prioritize the critical elements of your application, identify goals, and iteratively work towards achieving them.
00:12:59.720 Remember that tests may not be as bad as they seem if you're actively considering their design. Most people don’t give their tests this much thought, so just participating in evaluating how to improve them can be a win. As Martin Fowler said, 'Imperfect tests run frequently are much better than perfect tests that are never written.' Getting something functioning is crucial; you can continuously improve and refine it along the way.
00:14:05.000 We can apply this principle of simplicity and coherence wherever we go. I have created a number of minan stickers that reflect this value. If you'd like one, feel free to come and find me. Now, I’d like to take a moment to address any questions or reflections you might have as we wrap up. Thank you for your time!
Explore all talks recorded at MountainWest RubyConf 2014
+8