Thông tin tài liệu:
Growing Object-Oriented Software, Guided by Tests- P4: Test-Driven Development (TDD) hiện nay là một kỹ thuật được thành lập để cung cấp các phần mềm tốt hơn nhanh hơn. TDD là dựa trên một ý tưởng đơn giản: các bài kiểm tra Viết cho code của bạn trước khi bạn viết đoạn code riêng của mình. Tuy nhiên, điều này "đơn giản" ý tưởng có kỹ năng và bản án để làm tốt. Bây giờ có một tài liệu hướng dẫn thiết thực để TDD mà sẽ đưa bạn vượt ra ngoài những khái niệm cơ bản. Vẽ trên một...
Nội dung trích xuất từ tài liệu:
Growing Object-Oriented Software, Guided by Tests- P4126 Chapter 13 The Sniper Makes a Bid developers shouldn’t be shy about creating new types. We think Main still does too much, but we’re not yet sure how best to break it up. We decide to push on and see where the code takes us. Sending a Bid An Auction Interface The next step is to have the Sniper send a bid to the auction, so who should the Sniper talk to? Extending the SniperListener feels wrong because that relationship is about tracking what’s happening in the Sniper, not about making external commitments. In the terms defined in “Object Peer Stereotypes” (page 52), SniperListener is a notification, not a dependency. After the usual discussion, we decide to introduce a new collaborator, an Auction. Auction and SniperListener represent two different domains in the application: Auction is about financial transactions, it accepts bids for items in the market; and SniperListener is about feedback to the application, it reports changes to the current state of the Sniper. The Auction is a dependency, for a Sniper cannot function without one, whereas the SniperListener, as we discussed above, is not. Introducing the new interface makes the design look like Figure 13.2. Figure 13.2 Introducing Auction The AuctionSniper Bids Now we’re ready to start bidding. The first step is to implement the response to a Price event, so we start by adding a new unit test for the AuctionSniper. It says that the Sniper, when it receives a Price update, sends an incremented bid to the auction. It also notifies its listener that it’s now bidding, so we add a sniperBidding() method. We’re making an implicit assumption that the Auction knows which bidder the Sniper represents, so the Sniper does not have to pass in that information with the bid. Sending a Bid 127public class AuctionSniperTest { private final Auction auction = context.mock(Auction.class); private final AuctionSniper sniper = new AuctionSniper(auction, sniperListener); […] @Test public void bidsHigherAndReportsBiddingWhenNewPriceArrives() { final int price = 1001; final int increment = 25; context.checking(new Expectations() {{ one(auction).bid(price + increment); atLeast(1).of(sniperListener).sniperBidding(); }}); sniper.currentPrice(price, increment); }} The failure report is:not all expectations were satisfiedexpectations: ! expected once, never invoked: auction.bid() ! expected at least 1 time, never invoked: sniperListener.sniperBidding()what happened before this: nothing! When writing the test, we realized that we don’t actually care if the Snipernotifies the listener more than once that it’s bidding; it’s just a status update,so we use an atLeast(1) clause for the listener’s expectation. On the other hand,we do care that we send a bid exactly once, so we use a one() clause for its ex-pectation. In practice, of course, we’ll probably only call the listener once, butthis loosening of the conditions in the test expresses our intent about the tworelationships. The test says that the listener is a more forgiving collaborator, interms of how it’s called, than the Auction. We also retrofit the atLeast(1) clauseto the other test method. How Should We Describe Expected Values? We’ve specified the expected bid value by adding the price and increment.There are different opinions about whether test values should just be literals with “obvious” values, or expressed in terms of the calculation they represent. Writing out the calculation may make the test more readable but risks reimplementing the target code in the test, and in some cases the calculation will be too complicated to repro- duce. Here, we decide that the calculation is so trivial that we can just write it into the test.128 Chapter 13 The Sniper Makes a Bid jMock Expectations Don’t Need to Be Matched in Order This is our first test with more than one expectation, so we’ll point out that the order in which expectations are declared does not have to match the order in which the methods are called in the code. If the calling order does matter, the expectations should include a sequence clause, which is described in Appendix A. The implementation to make the test pass is simple. public interface Auction { void bid(int amount); } public class AuctionSniper implements AuctionEventListener { […] private final SniperListener sniperListener; private final Auction auction; pub ...