online shoes store xkshoes,here check the latest yeezy shoes click here.

know more about 2020 nike and adidas soccer cleats news,check shopcleat and wpsoccer.

Abstract vs. Concrete Parameters:
Contradictory Patterns for Testable Designs

Kent Beck, Three Rivers Institute
August 2008

 

Easy-to-test software is "controllable". Testers can cheaply and accurately simulate the contexts in which the software needs to run. Two contradictory patterns help achieve controllability: making parameters more concrete and more abstract. This apparent contradiction resolves when looked at from a broader perspective.

Introduction

Designing software is hard. Designs need to be understandable to those who will implement them, they need to support currently required functionality, and they need to support future changes, anticipated and unanticipated. The two economic imperatives of software development--rapid initial time to market and low lifecycle costs--often call for different designs. Designers need to find some resolution of these technical and economic conflicts.
    As if this wasn't difficult enough, Extreme Programming comes along and calls for software to be testable, automatically testable, as well. For software to be testable, it must be controllable and observable. Observability is the ability to measure the effects of a computation. I won't talk about more in this note. Controllability is the ability to reproduce all the contexts in which an object is to run, normal and abnormal. The new job of design is to make sure that software is controllable cheaply in both developer time and execution time.
    I wrote tests and programs together for years without explicitly worrying about controllability. One of the strengths of test-driven development is that using it provides immediate feedback about whether or not a proposed API can exercise an object and how difficult it is to use. In more complicated situations, though, or when dealing with legacy code, I've found thinking explicitly about controllability to be helpful.

Concrete Parameters Enhance Controllability

An early encounter with controllability came when I was working on an insurance system. We needed to be sure that we were retrieving the correct mortality table for a customer. Our first attempt at a test/interface pair was to pass the customer as a parameter:
class MortalityTableTest {
    @Test retrieveMaleNonSmoker() {
       // ... a bunch of stuff to create a male, non-smoking customer
       MortalityTable result= MortalityTable.lookup(customer);
       assertEquals("QM115", result.getName());
    }
}

    This worked, but it was expensive, both writing the code to create a customer and the execution time to create the megabyte of objects necessary to create a well-formed customer. This left the test slow and vulnerable to changes in the way we set up customers.
    Looking at the MortalityTable.lookup() code we could see that the only parts of a Customer we used were the gender and smoker status: two one bit enums out of a megabyte. Rather than wait and let the table lookup extract this information, we could shift that bit of code to the caller:
class MortalityTableTest {
    @Test retrieveMaleNonSmoker() {
       // ... a bunch of stuff to create a customer
       MortalityTable result= MortalityTable.lookup(customer.getGender(), customer.getSmoker());
       assertEquals("QM115", result.getName());
    }
}

The second step was to inline all the customer creation and gender/smoker retrieval, since we knew what the answers would be:
class MortalityTableTest {
    @Test retrieveMaleNonSmoker() {
       MortalityTable result= MortalityTable.lookup(Gender.MALE, Smoker.NO);
       assertEquals("QM115", result.getName());
    }
}

The resulting test is easier to read and runs fast. Changes to the Customer won't affect the test. However, the test is vulnerable to changes in the lookup process. In the first version, if the lookup began also checking for marital status we would only have to change the test to set the appropriate status. In the third version, we would not only have to change the test, we would also have to change the API of MortalityTable. As long as mortality table lookup remains stable, though, we improved the controllability of our system by passing more-concrete parameters.

Abstract Parameters Enhance Controllability

Recently, I had an experience that seemed to offer the opposite conclusion. A questioner to the JUnit mailing list asked about testing a legacy object that needed to communicate over a socket. The existing API took an IP address and port number. How could they write tests?
    A black box strategy is to create a test fixture that can open up server sockets simulating the various test conditions. This has the advantage that the object-under-test need not be modified. However, it would be a fairly complex fixture to write, making sure to completely clean up the test bed, correctly handle timeouts and avoid race conditions.
    An alternative is to objectify the IP address and port number into a SocketConnection. Rather than pass raw numbers into the constructor, pass a SocketConnection (an existing implementation, if possible). The current constructor can be grandfathered by having it create a connection:
class Client {
    Client(int address, int port) {
       this(new SocketConnection(address, port));
    }
}
Tests can then create impostor connections to simulate all the test scenarios. This solution will likely run faster and be easier to write tests for, but at the cost of modifying the Client. The big point, from the perspective of this paper, is that controllability in this case was achieved by passing a more abstract parameter.

Ambiguous? Maybe. Contradictory? No.

Here we run aground. In the first case, controllability came from making the parameters more concrete, in the second, from making them more abstract. Which is it? What is the simple rule?
    It turns out that in this situation, the rule for achieving controllability is not simple and linear. How best to design for controllability is the result of a tradeoff between the cost and the required flexibility of the tests. One leg of the tradeoff says that concrete parameters are more economical than abstract parameters:
concrete parameters cost less
    The second leg of the tradeoff says that the flexibility of tests are enhanced by abstract parameters:
abstract parameters are more flexible
    Put the two together and you have the tradeoff curve:
tradeoff
    As a designer, I find such tradeoffs to be extremely useful, especially as a way of explaining my decisions. I may get an intuition without explicitly thinking of the tradeoff, but when I want to discuss a decision I find it valuable to be able to say, "In this case, we really don't need flexibility, so concrete parameters are appropriate":
concrete parameters dominate
    Alternatively, if my gut tells me a more-abstract parameter would be an improvement, I like being able to illustrate it, "We have all these tests and all these parameters. I think it would be simpler to bundle them together.":
abstract parameters dominate
    Seeing the two factors together helps me be more aware of when I have an opportunity to improve my design by sliding towards abstract or concrete parameters.

Conclusion

Now I can state the general rule: to make software controllable, pass concrete parameters when tests don't need much flexibility and pass abstract parameters when they need more flexibility. Having just realized this relationship, though, I'm not sure how far it will go. If you find interesting examples (or counter-examples), I'd love to hear about them.