Tài liệu Growing Object-Oriented Software, Guided by Tests- P8 doc

35 322 1
Tài liệu Growing Object-Oriented Software, Guided by Tests- P8 doc

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

ptg brittle—they would misreport if the system changes the assumptions they’ve been built on. One response is to add a test to confirm those expectations—in this case, perhaps a stress test to confirm event processing order and alert the team if circumstances change. That said, there should already be other tests that confirm those assumptions, so it may be enough just to associate these tests, for example by grouping them in the same test package. Distinguish Synchronizations and Assertions We have one mechanism for synchronizing a test with its system and for making assertions about that system—wait for an observable condition and time out if it doesn’t happen. The only difference between the two activities is our interpre- tation of what they mean. As always, we want to make our intentions explicit, but it’s especially important here because there’s a risk that someone may look at the test later and remove what looks like a duplicate assertion, accidentally introducing a race condition. We often adopt a naming scheme to distinguish between synchronizations and assertions. For example, we might have waitUntil() and assertEventually() methods to express the purpose of different checks that share an underlying implementation. Alternatively, we might reserve the term “assert” for synchronous tests and use a different naming conventions in asynchronous tests, as we did in the Auction Sniper example. Externalize Event Sources Some systems trigger their own events internally. The most common example is using a timer to schedule activities. This might include repeated actions that run frequently, such as bundling up emails for forwarding, or follow-up actions that run days or even weeks in the future, such as confirming a delivery date. Hidden timers are very difficult to work with because they make it hard to tell when the system is in a stable state for a test to make its assertions. Waiting for a repeated action to run is too slow to “succeed fast,” to say nothing of an action scheduled a month from now. We also don’t want tests to break unpredictably because of interference from a scheduled activity that’s just kicked in. Trying to test a system by coinciding timers is just too brittle. The only solution is to make the system deterministic by decoupling it from its own scheduling. We can pull event generation out into a shared service that is driven externally. For example, in one project we implemented the system’s scheduler as a web service. System components scheduled activities by making HTTP requests to the scheduler, which triggered activities by making HTTP “postbacks.” In another project, the scheduler published notifications onto a message bus topic that the components listened to. Chapter 27 Testing Asynchronous Code 326 From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg With this separation in place, tests can step the system through its behavior by posing as the scheduler and generating events deterministically. Now we can run system tests quickly and reliably. This is a nice example of a testing require- ment leading to a better design. We’ve been forced to abstract out scheduling, which means we won’t have multiple implementations hidden in the system. Usually, introducing such an event infrastructure turns out to be useful for monitoring and administration. There’s a trade-off too, of course. Our tests are no longer exercising the entire system. We’ve prioritized test speed and reliability over fidelity. We compensate by keeping the scheduler’s API as simple as possible and testing it rigorously (another advantage). We would probably also write a few slow tests, running in a separate build, that exercise the whole system together including the real scheduler. 327 Externalize Event Sources From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg This page intentionally left blank From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg Afterword A Brief History of Mock Objects Tim Mackinnon Introduction The ideas and concepts behind mock objects didn’t materialise in a single day. There’s a long history of experimentation, discussion, and collaboration between many different developers who have taken the seed of an idea and grown it into something more profound. The final result—the topic of this book—should help you with your software development; but the background story of “The Making of Mock Objects” is also interesting—and a testament to the dedication of the people involved. I hope revisiting this history will inspire you too to challenge your thoughts on what is possible and to experiment with new practices. Origins The story began on a roundabout 1 near Archway station in London in late 1999. That evening, several members of a London-based software architecture group 2 met to discuss topical issues in software. The discussion turned to experiences with Agile Software Development and I mentioned the impact that writing tests seemed to be having on our code. This was before the first Extreme Programming book had been published, and teams like ours were still exploring how to do test-driven development—including what constituted a good test. In particular, I had noticed a tendency to add “getter” methods to our objects to facilitate testing. This felt wrong, since it could be seen as violating object-oriented princi- ples, so I was interested in the thoughts of the other members. The conversation was quite lively—mainly centering on the tension between pragmatism in testing and pure object-oriented design. We also had a recent example of a colleague, 1. “Roundabout” is the UK term for a traffic circle. 2. On this occasion, they were Tim Mackinnon, Peter Marks, Ivan Moore, and John Nolan. 329 From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg Oli Bye, stubbing out the Java Servlet API for testing a web application without a server. I particularly remember from that evening a crude diagram of an onion 3 and its metaphor of the many layers of software, along with the mantra “No Getters! Period!” The discussion revolved around how to safely peel back and test layers of that onion without impacting its design. The solution was to focus on the composition of software components (the group had discussed Brad Cox’s ideas on software components many times before). It was an interesting collision of opinions, and the emphasis on composition—now referred to as dependency injection—gave us a technique for eliminating the getters we were “pragmatically” adding to objects so we could write tests for them. The following day, our small team at Connextra 4 started putting the idea into practice. We removed the getters from sections of our code and used a composi- tional strategy by adding constructors that took the objects we wanted to test via getters as parameters. At first this felt cumbersome, and our two recent graduate recruits were not convinced. I, however, had a Smalltalk background, so to me the idea of composition and delegation felt right. Enforcing a “no getters” rule seemed like a way to achieve a more object-oriented feeling in the Java language we were using. We stuck to it for several days and started to see some patterns emerging. More of our conversations were about expecting things to happen between our objects, and we frequently had variables with names like expectedURL and expectedServiceName in our injected objects. On the other hand, when our tests failed we were tired of stepping through in a debugger to see what went wrong. We started adding variables with names like actualURL and actualServiceName to allow the injected test objects to throw exceptions with helpful messages. Printing the expected and actual values side-by-side showed us immediately what the problem was. Over the course of several weeks we refactored these ideas into a group of classes: ExpectationValue for single values, ExpectationList for multiple values in a particular order, and ExpectationSet for unique values in any order. Later, Tung Mac also added ExpectationCounter for situations where we didn’t want to specify explicit values but just count the number of calls. It started to feel as if something interesting was happening, but it seemed so obvious to me that there wasn’t really much to describe. One afternoon, Peter Marks decided that we should come up with name for what we were doing—so we could at least package the code—and, after a few suggestions, proposed “mock.” We could use it both as a noun and a verb, and it refactored nicely into our code, so we adopted it. 3. Initially drawn by John Nolan. 4. The team consisted of Tim Mackinnon, Tung Mac, and Matthew Cooke, with direction from Peter Marks and John Nolan. Connextra is now part of Bet Genius. Afterword A Brief History of Mock Objects 330 From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg Spreading the Word Around this time, we 5 also started the London Extreme Tuesday Club (XTC) to share experiences of Extreme Programming with other teams. During one meeting, I described our refactoring experiments and explained that I felt that it helped our junior developers write better object-oriented code. I finished the story by saying, “But this is such an obvious technique that I’m sure most people do it eventually anyway.” Steve pointed out that the most obvious things aren’t always so obvious and are usually difficult to describe. He thought this could make a great paper if we could sort the wood from the trees, so we decided to collaborate with another XTC member (Philip Craig) and write something for the XP2000 conference. If nothing else, we wanted to go to Sardinia. We began to pick apart the ideas and give them a consistent set of names, studying real code examples to understand the essence of the technique. We backported new concepts we discovered to the original Connextra codebase to validate their effectiveness. This was an exciting time and I recall that it took many late nights to refine our ideas—although we were still struggling to come up with an accurate “elevator pitch” for mock objects. We knew what it felt like when using them to drive great code, but describing this experience to other developers who weren’t part of the XTC was still challenging. The XP2000 paper [Mackinnon00] and the initial mock objects library had a mixed reception—for some it was revolutionary, for others it was unnecessary overhead. In retrospect, the fact that Java didn’t have good reflection when we started meant that many of the steps were manual, or augmented with code generation tools. 6 This turned people off—they couldn’t separate the idea from the implementation. Another Generation The story continues when Nat Pryce took the ideas and implemented them in Ruby. He exploited Ruby’s reflection to write expectations directly into the test as blocks. Influenced by his PhD work on protocols between components, his li- brary changed the emphasis from asserting parameter values to asserting messages sent between objects. Nat then ported his implementation to Java, using the new Proxy type in Java 1.3 and defining expectations with “constraint” objects. When Nat showed us this work, it immediately clicked. He donated his library to the mock objects project and visited the Connextra offices where we worked together to add features that the Connextra developers needed. 5. With Tim Mackinnon, Oli Bye, Paul Simmons, and Steve Freeman. Oli coined the name XTC. 6. This later changed as Java 1.1 was released, which improved reflection, and as others who had read our paper wrote more tools, such as Tammo Freese’s Easymock. 331 Another Generation From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg With Nat in the office where mock objects were being used constantly, we were driven to use his improvements to provide more descriptive failure messages. We had seen our developers getting bogged down when the reason for a test failure was not obvious enough (later, we observed that this was often a hint that an object had too many responsibilities). Now, constraints allowed us to write tests that were more expressive and provided better failure diagnostics, as the constraint objects could explain what went wrong. 7 For example, a failure on a stringBegins constraint could produce a message like: Expected a string parameter beginning with "http" but was called with a value of "ftp.domain.com" We released the new improved version of Nat’s library under the name Dynamock. As we improved the library, more programmers started using it, which intro- duced new requirements. We started adding more and more options to the API until, eventually, it became too complicated to maintain—especially as we had to support multiple versions of Java. Meanwhile, Steve tired of the the duplication in the syntax required to set up expectations, so he introduced a version of a Smalltalk cascade—multiple calls to the same object. Then Steve noticed that in a statically typed language like Java, a cascade could return a chain of interfaces to control when methods are made available to the caller—in effect, we could use types to encode a workflow. Steve also wanted to improve the programming experience by guiding the new generation of IDEs to prompt with the “right” completion options. Over the course of a year, Steve and Nat, with much input from the rest of us, pushed the idea hard to produce jMock, an expressive API over our original Dynamock framework. This was also ported to C# as NMock. At some point in this process, they realized that they were actually writing a language in Java which could be used to write expectations; they wrote this up later in an OOPLSA paper [Freeman06]. Consolidation Through our experience in Connextra and other companies, and through giving many presentations, we improved our understanding and communication of the ideas of mock objects. Steve (inspired by some of the early lean software material) coined the term “needs-driven development,” and Joe Walnes, another colleague, drew a nice visualisation of islands of objects communicating with each other. Joe also had the insight of using mock objects to drive the design of interfaces between objects. At the time, we were struggling to promote the idea of using mock objects as a design tool; many people (including some authors) saw it only as a technique for speeding up unit tests. Joe cut through all the conceptual barriers with his simple heuristic of “Only mock types you own.” 7. Later, Steve talked Charlie Poole into including constraints in NUnit. It took some extra years to have matchers (the latest version of constraints) adopted by JUnit. Afterword A Brief History of Mock Objects 332 From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg We took all these ideas and wrote a second conference paper, “Mock Roles not Objects” [Freeman04]. Our initial description had focused too much on im- plementation, whereas the critical idea was that the technique emphasizes the roles that objects play for each other. When developers are using mock objects well, I observe them drawing diagrams of what they want to test, or using CRC cards to roleplay relationships—these then translate nicely into mock objects and tests that drive the required code. Since then, Nat and Steve have reworked jMock to produce jMock2, and Joe has extracted constraints into the Hamcrest library (now adopted by JUnit). There’s also now a wide selection of mock object libraries, in many different languages. The results have been worth the effort. I think we can finally say that there is now a well-documented and polished technique that helps you write better soft- ware. From those humble “no getters” beginnings, this book summarizes years of experience from all of us who have collaborated, and adds Steve and Nat’s language expertise and careful attention to detail to produce something that is greater than the sum of its parts. 333 Consolidation From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg This page intentionally left blank From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. ptg Appendix A jMock2 Cheat Sheet Introduction We use jMock2 as our mock object framework throughout this book. This chapter summarizes its features and shows some examples of how to use them. We’re using JUnit 4.6 (we assume you’re familiar with it); jMock also supports JUnit3. Full documentation is available at www.jmock.org. We’ll show the structure of a jMock unit test and describe what its features do. Here’s a whole example: import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.integration.junit4.JMock; import org.jmock.integration.junit4.JUnit4Mockery; @RunWith(JMock.class) public class TurtleDriverTest { private final Mockery context = new JUnit4Mockery(); private final Turtle turtle = context.mock(Turtle.class); @Test public void goesAMinimumDistance() { final Turtle turtle2 = context.mock(Turtle.class, "turtle2"); final TurtleDriver driver = new TurtleDriver(turtle1, turtle2); // set up context.checking(new Expectations() {{ // expectations ignoring (turtle2); allowing (turtle).flashLEDs(); oneOf (turtle).turn(45); oneOf (turtle).forward(with(greaterThan(20))); atLeast(1).of (turtle).stop(); }}); driver.goNext(45); // call the code assertTrue("driver has moved", driver.hasMoved()); // further assertions } } 335 From the Library of Lee Bogdanoff Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... TRUSTED TECHNOLOGY LEARNING SOURCE From the Library of Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark Lee Bogdanoff FREE Online Edition Your purchase of Growing Object-Oriented Software, Guided by Tests includes access to a free online edition for 45 days through the Safari Books Online subscription service Nearly every Addison-Wesley Professional book is available online... expectation can also specify an action to perform when it is matched, by adding a will() clause after the invocation For example, this expectation will return PEN_DOWN when queryPen() is called: allowing (turtle).queryPen(); will(returnValue(PEN_DOWN)); jMock provides several standard actions, and programmers can provide custom actions by implementing the Action interface The standard actions are: will(returnValue(v))... purchase PDF Split-Merge on www.verypdf.com to remove the Library of Lee Bogdanoff this watermark A New Matcher Type 345 The text generated by the describeTo() and describeMismatch() must follow certain grammatical conventions to fit into the error messages generated by JUnit and jMock Although JUnit and jMock generate different messages, matcher descriptions that complete the sentence “expected description... [Wirfs-Brock03] Wirfs-Brock, Rebecca and Alan McKean Object Design: Roles, Responsibilities, and Collaborations Addison-Wesley, 2003, ISBN 0201379430 [Woolf98] Woolf, Bobby “Null Object.” In: Pattern Languages of Program Design 3 Edited by Robert Martin, Dirk Riehle, and Frank Buschmann Addison-Wesley, 1998, http://www.cse.wustl.edu/~schmidt/PLoP-96/woolf1.ps.gz [Yourdon79] Yourdon, Edward and Larry Constantine... Library of Lee Bogdanoff this watermark Index O object mother pattern, 257–258 object-oriented programming, 13, 329 objects abstraction level of, 57 bringing out relationships between, 236 collaborating, 18–20, 52–53, 58, 60–62, 186 communicating, 13–14, 50, 58, 244–245 composite, 53–54 context-independent, 54–55, 233 created by builders, 259–260 difficult to decouple, 273 mutable, 14 sharing references... specified by calling the method on the mock object within an expectation block This defines the name of the method and what argument values are acceptable Values passed to the method in an expectation will be compared for equality: oneOf (turtle).turn(45); // matches turn() called with 45 oneOf (calculator).add(2, 2); // matches add() called with 2 and 2 Invocation matching can be made more flexible by using... States Invocations can be constrained to occur only when a condition is true, where a condition is defined as a state machine that is in a given state State machines can switch between states specified by state names A test can create multiple state machines, and an invocation can be constrained to one or more conditions The syntax for creating a state machine is: States state-machine-name = context.states("state-machine-name").startsAs("initial-state");...336 Appendix A jMock2 Cheat Sheet Test Fixture Class First, we set up the test fixture class by creating its Mockery import import import import org.jmock.Expectations; org.jmock.Mockery; org.jmock.integration.junit4.JMock; org.jmock.integration.junit4.JUnit4Mockery; @RunWith(JMock.class) public... Computer Programs MIT Press, 1996, ISBN 978-0262011532 [Beck99] Beck, Kent Extreme Programming Explained: Embrace Change AddisonWesley, 1999, ISBN 978-0321278654 [Beck02] Beck, Kent Test Driven Development: By Example Addison-Wesley, 2002, ISBN 978-0321146530 [Begel08] Begel, Andrew and Beth Simon “Struggles of New College Graduates in Their First Software Development Job.” In: SIGCSE Bulletin, 40, no 1 (March... http://alistair.cockburn.us/ Hexagonal+architecture [Cohn05] Cohn, Mike Agile Estimating and Planning Prentice Hall, 2005, ISBN 978-0131479418 [Demeyer03] Demeyer, Serge, Stéphane Ducasse, and Oscar Nierstrasz Object-Oriented Reengineering Patterns http://scg.unibe.ch/download/oorp/ [Evans03] Evans, Eric Domain-Driven Design: Tackling Complexity in the Heart of Software Addison-Wesley, 2003, ISBN 978-0321125217 . and implemented them in Ruby. He exploited Ruby’s reflection to write expectations directly into the test as blocks. Influenced by his PhD work on protocols. service. System components scheduled activities by making HTTP requests to the scheduler, which triggered activities by making HTTP “postbacks.” In another project,

Ngày đăng: 24/12/2013, 06:17

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan