In the video embedded in the previous blog post, The Foot-In-The-Door Technique, we saw how two researchers in the 1960s discovered that you can change an aspect of somebody’s identity by making a small request that is easy to say yes to. In the embedded video above, I examine how we can apply the “foot-in-the-door” technique to grow a test automation engineer’s sense of ownership with the help of a skill called, “listening to the tests.”
The foot-in-the-door technique demonstrated that a way to forge a new identity for someone is to get them to make some small change that starts them thinking, “Yes, I am the kind of person who does things like this.” This, in turn, makes the larger change easier to make because it doesn’t conflict with the new identity that is forming.
Hybrid Engineers
A great example of this was my own employer’s transition of software test automation engineers and software development engineers into a hybrid engineer role. For software test automation engineers, who formerly worked exclusively on test code and didn’t touch product code, this represented a big change. The software development engineers, who were not used to writing automated tests for their own code, I would argue faced an even bigger change, but today I want to focus on the software test automation engineers.
A key aspect of their new identity was that they would now be modifying the source code of the product itself and not just its automated tests. For automation engineers who were used to working around the lack of testability in product developers’ code, this represented a big change in identity. For these automation engineers, a foot in the door that would start to change their identity towards this new, hybrid mindset, is something called “listening to the tests.”
Listening To The Tests
I took this term from the book, Growing Object-Oriented Software Guided By Tests by Nat Pryce and Steve Freeman. They say, “When writing unit and integration tests, we stay alert for areas of the code that are difficult to test. When we find a feature that’s difficult to test, we don’t just ask ourselves how to test it, but also why is it difficult to test.”
Let me illustrate this with an example: Let’s say you want to lay out six buttons on a form in two side-by-side columns of three buttons each. We might structure this as follows: the main form contains a horizontal layout. In this horizontal layout are two side-by-side vertical layouts. In the first vertical layout, there are buttons one through three. In the second vertical layout, there are buttons four through six.
Computing Desired Widths
One of the things a graphical user interface needs to figure out is how to size each component to fit on the screen. Components often can return a desired width and height that helps the overall calculation. For example, a button with a large amount of text on it has a greater desired width than a button with a short string of text. Depending on how those buttons are laid out on a form, they can have different effects on the overall width calculation. In a vertical layout, for example, the desired width of the widest button could become the desired width of the overall vertical layout. In a horizontal layout, the desired width of the overall horizontal layout is the sum of the desired widths of the buttons.
A software development engineer might code up the aforementioned form as follows:
- Create the form.
- Add the horizontal layout component to the form.
- Add the two vertical components inside the horizontal component.
- Add each of the first three buttons to the first vertical layout component.
- Add each of the last three buttons to the second vertical layout component.
Breaking Encapsulation
Now we get to the width calculation and this is where a development engineer can make things hard for the test automation engineer and at the same time make things hard for themselves. A development engineer may decide to calculate the desired width of the overall form in one place and individually inspect the internal guts of each component to compute the overall desired width. This is poor encapsulation and leads to harder-to-maintain code, but sometimes this is the situation a test automation engineer finds themselves in.
Now let’s imagine we are the test engineer trying to write an automated test of the width calculation. Assuming we can’t just instantiate buttons due to, say, a dependency on the graphical user interface being started up, we decide to stub the user interface components to control the individual buttons’ desired widths. Because of the way the code was designed, we’re going to have to do a lot of stubbing.
Using Mockito, we can stub the horizontal layout, the two vertical layouts, and all six buttons. We want to test that we are calculating the max for each button in a given vertical layout, so we want to choose a desired width for each button to exercise this max calculation. We also need to test that these two maximums are being summed to produce the final answer. We also need to make sure that the stub horizontal layout is returning the two stub vertical layouts and that the two stub vertical layouts are each returning the stub buttons. Finally, we can write our assertion the the form’s overall desired width is the sum of the maximums of the buttons’ desired widths.
This is one ugly, convoluted test. The intent is obscured by all the setup code. A test automation engineer, listening to this test, should be thinking, “This was difficult to test. Why?” At this point, the test automation engineer should have the confidence to suggest a new, more testable and more maintainable design and could work with the development engineer to refactor the code.
Refactoring To Encapsulate
As a first step, the width calculation could be pushed into the horizontal layout, simplifying the calculation of the form’s desired width to be simply the desired width of its one and only horizontal layout component.
Next, we would need to test the horizontal layout component’s getDesiredWidth method. For this method, we want to test that it returns the sum of the desired widths of its vertical layout components. So we stub two vertical components, instantiate the horizontal component, stub values for the two vertical layout components’ desired widths, add the vertical layout components to the horizontal layout component, and assert that the horizontal component’s desired width is the sum of the vertical components’ desired widths. Once again, this test is much simpler and so is the corresponding code.
Finally, we need to test the vertical component’s getDesiredWidth method. We want to test that it returns the maximum of the desired widths of all the buttons that have been added to it. So we stub the buttons, instantiate the vertical component, stub values for the two buttons’ desired widths, add the buttons to the vertical component, and assert that the vertical component’s desired width is the maximum of the buttons’ desired widths. The test is again simple because it only has to deal with the vertical component and its buttons.
The Benefits To The Development Engineer
Now let’s look at who benefits from this new behavior of the test automation engineer. The development engineer benefits because their code is now:
- more testable
- simpler
- more maintainable
- more adaptable to change
It is more adaptable to change because no object knows too much about the details of any other object. If, say, the horizontal component started calculating the width of a border in its width calculation, the form and vertical components do not need to change to accommodate this and neither do their tests.
The Benefits To The Test Engineer
The test automation engineer also benefits. The tests are simpler and more maintainable. Somebody new joining the team can look at these tests and quickly understand how each component computes its desired width. The test automation engineer also benefits because they are taking some responsibility for the overall design of the product and increasing their sense of ownership. This change in identity from one in which the sense of ownership extended only to the tests themselves to one in which it extends to the entire product, tests and all, is the foot in the door to an identity as a hybrid engineer.
I would love to hear what you thought of the video and blog post, so feel free to comment below, on The K Guy Twitter, or on The K Guy Facebook fan page.