RailsConf 2013

Testing Complex Systems: Creating data and limiting scope

Testing Complex Systems: Creating data and limiting scope

by Noel Rappin

The video titled "Testing Complex Systems: Creating data and limiting scope" features Noel Rappin and was presented during the Rails Conf 2013. The workshop focuses on the challenges encountered when testing complex systems, particularly in creating useful test data and limiting the scope of tests to specific parts of the codebase. Rappin emphasizes the importance of test-driven development (TDD) in driving modular design and clarifying requirements.

Key discussion points include:
- Creating Test Data: Test data should be easy to set up and robust to prevent tests from failing due to trivial reasons. Rappin discusses issues with entangled data when testing complex interactions, such as creating multiple objects just to run a unit test.
- Limiting Tests: It's crucial to isolate tests from unrelated parts of the code to avoid failure from external issues. Effective testing should ensure that failures indicate specific problems within the tested unit.
- Driving Modular Design: Rappin suggests using TDD to create a modular design where changes in one area of the system do not cause cascading failures in tests related to other parts.
- Data Creation Tools: The presenter highlights tools like factories and fixtures for setting up test data. Factories allow flexibility and readability while maintaining the ability to override data, whereas fixtures can be brittle and make tests harder to read.
- Using Mock Objects: Mocking can simplify tests by substituting real objects with stubs that return predefined values, thus focusing on specific aspects of the system being tested.
- Speeding Up Tests: Emphasizing fast tests is crucial in a TDD environment, as slower tests can lead to less engagement with the testing process.

The workshop culminates in the realization that maintaining a robust and efficient test suite is essential for developers to foster confidence in code changes. Overall, Rappin articulates that the greatest challenge lies in striking a balance between thorough testing and a pragmatic approach to maintain workflow efficiency.

Rappin encourages attendees to apply the techniques discussed in practical scenarios, reinforcing the idea that testing should facilitate development rather than obstruct it. In conclusion, effective testing enables developers to ensure the quality of their code while adapting to changes with ease.

00:00:16.480 Hi, just getting the mic in order here. I apologize in advance; my slide presentation software seems to not really like this resolution, so we're probably going to be dealing with it all afternoon, but we'll get by.
00:00:34.960 How many people here consider themselves testers? I should say, how many people here use testing in development in any way, shape, or form at any time in their lives? How many people use RSpec? How many people test regularly?
00:00:56.559 I could do this all day, frankly, but okay, what we're going to talk about here is two challenging problems in testing.
00:01:10.560 The first problem is creating data for tests, meaning you want data that’s easy to set up and fairly robust to prevent your tests from failing for trivial reasons. We're going to cover some techniques for doing that.
00:01:20.320 We're also going to talk about limiting tests to a particular part of the code. How many people were in Sandy's talk this afternoon? Almost everybody, that's good. She talked a lot about unit tests in terms of having a test focused on a particular part of the problem.
00:01:30.400 One thing you don’t want is for a test to fail because another part of the code has a problem. You don't want your tests to fail for reasons unrelated to their purpose.
00:01:40.080 Now, it turns out that one reason creating test data is hard is that when there are numerous tests involved, they often have entangled data. For instance, if you want to create a purchase in a unit test, you may also need to create a user, a product, a line item, and other complex object tree stuff.
00:01:55.520 Similarly, limiting your tests to a specific part of the code is challenging when your objects are entangled. If testing my purchase code requires calling various methods that belong to other classes, those are potential breaking points for tests.
00:02:24.959 So, the bright idea here is to keep our objects from being entangled. One way to do that in a Test-Driven Development (TDD) system is by driving the design with tests.
00:02:30.400 Therefore, we'll discuss one major problem: how to drive modular design with tests.
00:02:55.360 Now, a couple of things I'd like to point out at this point. There's a quirk about teaching TDD; I've taught test-driven development for years. One sticking point is that when explaining TDD step by step, taking five minutes to elaborate on each line of code makes it feel very slow.
00:03:01.840 Trust me when I say that strict TDD in practice is much faster, but when you have to explicitly explain every test and every line, we quickly find ourselves 20 minutes in with just three lines of code.
00:03:15.360 And people often wonder what's the point if it takes that long to write just three lines of code. Just remember, while writing it may take time, it will be the most valuable time spent.
00:03:25.280 This example is, of course, just an example. It is purposely missing aspects like security and design elements that would turn it into a full web application.
00:03:39.759 As we know, testing is considered a hallmark of professionalism in this community, so hopefully, you will learn how to do it better.
00:03:50.560 Now, let me get the application running.
00:03:53.520 If you have the app running, it should look a little like this. This is a travel agency that caters to time travelers.
00:04:11.040 The website primarily consists of trips, each with a start and end date, which indicate the time travel dates. Each trip can feature one or more hotel options and multiple activities that users can choose to purchase.
00:04:41.280 What we want to focus on here is users purchasing a trip. The requirement is to unify a user, a trip, a hotel, and a set of days that the user will be staying.
00:04:56.080 We also want to calculate the overall price of the trip, determine if the hotel is available, and run this through a pretend purchase gateway.
00:05:18.240 However, we're not going to worry about the view logic right now.
00:05:27.640 Let’s discuss where we might start with this requirement. Where would you start? What would be the first thing you might consider doing?
00:05:38.000 How about adding a trip to a cart? Let's assume for the moment that it’s just a button and we're just going to go straight to the purchase.
00:05:54.240 We can start from the top with an end-to-end test that covers the entire span of the feature, or we can start from the bottom and build up individual units.
00:06:06.560 The other option is to dive right in, not test, and come back later.
00:06:16.079 What's wrong with diving right in? You might think that it's easy and that you don't necessarily need tests for things you think you understand.
00:06:29.920 Well, the value of testing is that it helps clarify and finalize your requirements. It also assists in driving the design.
00:06:42.560 Sometimes, overseeing something may appear simple but can become complex. You end up having to look closer.
00:06:52.960 Look, it's much harder to build a test into a system that has already been built without tests.
00:07:04.800 So, as we work bottom-up, we need to outline the pieces of the functionality.
00:07:15.560 Specifically, we need to think about calculating the price, validating whether the hotel is available, and validating payments against a pretend gateway.
00:07:31.840 A good starting point would be calculating the price. What’s a good place to begin?
00:07:42.240 Considering both initialized state testing and happy path testing, let’s think about what's possible.
00:07:54.760 You can start with an initialized state test, where you create a new user with no purchases.
00:08:05.280 Then, another good place would be the basic happy path—testing the simplest piece of data that works.
00:08:20.760 It's also advantageous to think about what might make your tests fail and addressing special cases or error conditions.
00:08:29.640 So if you go into the actual travel piece, we might create the most basic way to do this. It’s about as simple a test as I can create.
00:08:44.400 We can clean this up by making sure we’re creating a trip and we're using RSpec to create this trip.
00:09:06.240 We’re going to set up the purchase with that trip.
00:09:25.600 What would be the simplest way to have this pass? Well, just returning the 200 would work.
00:09:42.240 The advantage of returning that value is that it drives quality since we start testing against real values.
00:09:52.000 In practice, you always want to have a failure first to ensure comprehensive testing.
00:10:08.760 Ultimately, if we drive tests incrementally, we build quality into our tests and design.
00:10:17.400 We're also observing the pattern of ensuring tests fail when they should, rather than having hidden issues.
00:10:34.800 Building the correct tests allows us to express intent clearly in the code while also making alterations easier down the line.
00:10:49.600 So we will test for hotel validity based on prices.
00:10:59.360 When creating the stubs for price, we know to pay attention to making clear assertions in our tests.
00:11:11.920 This builds flexibility, as we can adjust to changes in other areas of testing.
00:11:27.440 As we proceed with these tests, we need to keep track of their effectiveness as they adjust with project changes.
00:11:41.760 Best practices entail being careful about stubbing and managing potential complications in test integration.
00:11:59.840 Ultimately, we acknowledge that while approaches like factories and mocks can be powerful, they carry their potential risks.
00:12:10.960 Every time you create associations, it can pile up additional complexity.
00:12:31.440 In conclusion, it helps to break down obstacles blocking an easy path to quality tests by wrapping and structuring correctly.
00:12:44.960 That will lead to the success of your testing workflow.
00:12:54.960 Remember, clarity and simplicity are your friends in maintaining effective tests.
00:12:59.200 Thank you for spending this time with me. I hope you found this valuable!
00:13:08.480 Have a great rest of the conference!