Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 95 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
95
Dung lượng
787,94 KB
Nội dung
508 Chapter 22 Fixture Teardown Patterns by all Testcase Classes, and would use a generic object destruction mechanism so that it would not have to care what types of objects it was deleting. protected void tearDown() throws Exception { removeObjects(allAirportIds, "Airport"); removeObjects(allFlights, "Flight"); } public void removeObjects(List objectsToDelete, String type) { Iterator i = objectsToDelete.iterator(); while (i.hasNext()) { try { BigDecimal id = (BigDecimal) i.next(); if ("Airport"==type) { facade.removeAirport(id); } else { facade.removeFlight(id); } } catch (Exception e) { // do nothing if the remove failed } } } If we were tearing down a Shared Fixture, we would annotate our tearDown method with the suitable annotation or attribute (e.g., @afterClass or [TestFixtureTearDown]) or move it to a Setup Decorator. Example: Automated Exercise Teardown If we wanted to take the next step and automatically tear down any objects created within the SUT, we could modify the SUT to use an observable Object Factory. In our test, we would add the following code: ResourceTracker tracker; public void setUp() { tracker = new ResourceTracker(); ObjectFactory.addObserver(tracker); } public void tearDown() { tracker.cleanup(); ObjectFactory.removeObserver(tracker); } This last example assumes that the Automated Teardown logic has been moved into the cleanup method on the ResourceTracker. Automated Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 509 In-line Teardown How do we tear down the Test Fixture? We include teardown logic at the end of the Test Method immediately after the result verifi cation. A large part of making tests repeatable and robust is ensuring that the test fi xture is torn down after each test and a new one created for the next test run. This strategy is known as a Fresh Fixture (page 311). Leftover objects and database records, as well as open fi les and connections, can at best cause performance degradations and at worst cause tests to fail or systems to crash. While some of these resources may be cleaned up automatically by garbage collection, others may be left hanging if they are not torn down explicitly. At a minimum, we should write In-line Teardown code that cleans up resources left over after our test. How It Works As we write a test, we mentally keep track of all objects the test creates that will not be cleaned up automatically. After writing the code to exercise the SUT and verify the outcome, we add logic to the end of the Test Method (page 348) to destroy any objects that will not be cleaned up automatically by the garbage collector. We use the relevant language feature to ensure that the teardown code is run regardless of the outcome of the test. Fixture SUT Teardown Testcase Class tearDown test_method_1 test_method_2 test_method_n Teardown Teardown Fixture SUT Teardown Testcase Class tearDown test_method_1 test_method_2 test_method_n Teardown Teardown In-line Teardown In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 510 Chapter 22 Fixture Teardown Patterns When to Use It We should use some form of teardown logic whenever we have resources that will not be freed automatically after the Test Method is run; we can use In-line Teardown when each test has different objects to clean up. We may discover that objects need to be cleaned up because we have Unrepeatable Tests (see Erratic Test on page 228) or Slow Tests (page 253) caused by the accumulation of detritus from many test runs. Unlike fi xture setup, the teardown logic is not important from the perspective of Tests as Documentation (see page 23). Use of any form of teardown logic may potentially contribute to High Test Maintenance Cost (page 265) and should be avoided if at all possible. Thus the only real benefi t of including the teardown logic on an in-line basis is that it may make it easier to maintain the teardown logic—a pretty slim benefi t, indeed. It is almost always better to strive for Automated Tear- down (page 503) or to use Implicit Teardown (page 516) if we are using Testcase Class per Fixture (page 631), where all tests in a Testcase Class (page 373) have the same test fi xture. We can also use In-line Teardown as a steppingstone to Implicit Teardown, thereby following the principle of “the simplest thing that could possibly work.” First, we learn how to do In-line Teardown for each Test Method; next, we extract the common logic from those tests into the tearDown method. We should not use In-line Teardown if the objects created by the test are subject to automated memory management. In such a case, we should use Garbage- Collected Teardown (page 500) instead because it is much less error-prone and keeps the tests easier to understand and maintain. Implementation Notes The primary consideration in In-line Teardown is ensuring that the teardown code actually runs even when the test is failed by an Assertion Method (page 362) or ends in an error in either the SUT or the Test Method. A secondary consider- ation is ensuring that the teardown code does not introduce additional errors. The key to doing In-line Teardown correctly is to use language-level constructs to ensure that the teardown code is run. Most modern languages include some sort of error/exception-handling construct that will attempt the execution of a block of code with the guarantee that a second block of code will be run regard- less of how the fi rst block terminates. In Java, this construct takes the form of a try block with an associated fi nally block. In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 511 Variation: Teardown Guard Clause To protect against a failure caused by trying to tear down a resource that doesn’t exist, we can put a “guard clause” around the logic. Its inclusion reduces the likelihood of a test error caused by the teardown logic. Variation: Delegated Teardown We can move much of the teardown logic out of the Test Method by calling a Test Utility Method (page 599). Although this strategy reduces the amount of teardown logic cluttering the test, we still need to place an error-handling con- struct around at least the assertions and the exercising of the SUT to ensure that it gets called. Using Implicit Teardown is almost always a better solution. Variation: Naive In-line Teardown Naive In-line Teardown is what we have when we forget to put the equivalent of a try/fi nally block around our test logic to ensure that our teardown logic always executes. It leads to Resource Leakage (see Erratic Test), which in turn may lead to Erratic Tests. Motivating Example The following test creates a persistent object (airport) as part of the fi xture. Because the object is stored in a database, it is not subject to Garbage-Collected Teardown and must be explicitly destroyed. If we do not include teardown logic in the test, each time the test is run it will create another object in the database. This may lead to Unrepeatable Tests unless the test uses Distinct Generated Values (see Generated Value on page 723) to ensure that the created objects do not violate any unique key constraints. public void testGetFlightsByOriginAirport_NoFlights_ntd() throws Exception { // Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF"); // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(outboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); } In-line Teardown In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 512 Chapter 22 Fixture Teardown Patterns Example: Naive In-line Teardown In this naive solution to this problem, we added a line after the assertion to destroy the airport created in the fi xture setup. public void testGetFlightsByOriginAirport_NoFlights() throws Exception { // Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF"); // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(outboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); facade.removeAirport(outboundAirport); } Unfortunately, this solution isn’t really adequate because the teardown logic won’t be exercised if the SUT encounters an error or if the assertions fail. We could try moving the fi xture cleanup before the assertions but this still wouldn’t address the issue with errors occurring inside the SUT. Clearly, we need a more general solution. Refactoring Notes We need either to place an error-handling construct around the exercising of the SUT and the assertions or to move the teardown code into the tearDown method. Either way, we need to ensure that all the teardown code runs, even if some parts of it fail. This usually involves the judicious use of try/fi nally control structures around each step of the teardown process. Example: In-line Teardown In this Java example, we have introduced a try/fi nally block around the exercise SUT and result verifi cation phases of the test to ensure that our teardown code is run. public void testGetFlightsByOriginAirport_NoFlights_td() throws Exception { // Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF"); try { // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(outboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 513 } finally { facade.removeAirport(outboundAirport); } } Now the exercising of the SUT and the assertions both appear in the try block and the teardown logic is found in the fi nally block. This separation is crucial to making In-line Teardown work properly. We should not include a catch block unless we are writing an Expected Exception Test (see Test Method). Example: Teardown Guard Clause Here, we’ve added a Teardown Guard Clause to the teardown code to ensure it isn’t run if the airport doesn’t exist: public void testGetFlightsByOriginAirport_NoFlights_TDGC() throws Exception { // Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF"); try { // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(outboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); } finally { if (outboundAirport!=null) { facade.removeAirport(outboundAirport); } } } Example: Multiresource In-line Teardown (Java) If multiple resources need to be cleaned up in the same test, we must ensure that all the teardown code runs even if some of the teardown statements contain errors. This goal can be accomplished by nesting each subsequent teardown step inside another block of guaranteed code, as in this Java example: public void testGetFlightsByOrigin_NoInboundFlight_SMRTD() throws Exception { // Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF"); BigDecimal inboundAirport = null; FlightDto expFlightDto = null; try { inboundAirport = createTestAirport("1IF"); expFlightDto = createTestFlight(outboundAirport, inboundAirport); In-line Teardown In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 514 Chapter 22 Fixture Teardown Patterns // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(inboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); } finally { try { facade.removeFlight(expFlightDto.getFlightNumber()); } finally { try { facade.removeAirport(inboundAirport); } finally { facade.removeAirport(outboundAirport); } } } } This scheme gets very messy in a hurry if we must clean up more than a few resources. In such a situation, it makes more sense to organize the resources into an array or list and then to iterate over that array or list. At that point we are halfway to implementing Automated Teardown. Example: Delegated Teardown We can also delegate the teardown from within the Test Method if we don’t believe we can come up with a completely generic way cleanup strategy that will work for all tests. public void testGetFlightsByOrigin_NoInboundFlight_DTD() throws Exception { // Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF"); BigDecimal inboundAirport = null; FlightDto expectedFlightDto = null; try { inboundAirport = createTestAirport("1IF"); expectedFlightDto = createTestFlight( outboundAirport, inboundAirport); // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(inboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); } finally { teardownFlightAndAirports( outboundAirport, inboundAirport, expectedFlightDto); } } In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 515 private void teardownFlightAndAirports( BigDecimal firstAirport, BigDecimal secondAirport, FlightDto flightDto) throws FlightBookingException { try { facade.removeFlight( flightDto.getFlightNumber() ); } finally { try { facade.removeAirport(secondAirport); } finally { facade.removeAirport(firstAirport); } } } The optimizers among us will notice that the two fl ight numbers are actually available as attributes of the fl ightDto. The paranoid will counter that because the teardownFlightAndAirports method could be called before the fl ightDto is constructed, we cannot count on using it to access the Airports. Hence we must pass the Airports in individually. The need to think this way is why a generic Automated Teardown is so attractive; it avoids having to think at all! In-line Teardown In-line Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 516 Chapter 22 Fixture Teardown Patterns Implicit Teardown How do we tear down the Test Fixture? The Test Automation Framework calls our cleanup logic in the tearDown method after every Test Method. A large part of making tests repeatable and robust is ensuring that the test fi xture is torn down after each test and a new one created for the next test run. This strategy is known as a Fresh Fixture (page 311). Leftover objects and database records, as well as open fi les and connections, can at best cause performance degradations and at worst cause tests to fail or systems to crash. When we can’t take advantage of Garbage-Collected Teardown (page 500) and we have several tests with the same objects to tear down, we can put the teardown logic into a special tearDown method that the Test Automation Framework (page 298) calls after each Test Method (page 348) is run. How It Works Anything that needs to be cleaned up can be freed or destroyed in the fi nal phase of the Four-Phase Test (page 358)—namely, the fi xture teardown phase. Most members of the xUnit family of Test Automation Frameworks support Implicit Teardown Fixture Testcase Class SUT Teardown test_1 test_2 test_n Teardown Also known as: Hooked Teardown, Framework- Invoked Teardown, Teardown Method Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 517 the concept of Implicit Teardown, wherein they call the tearDown method of each Testcase Object (page 382) after the Test Method has been run. The tearDown method is called regardless of whether the test passes or fails. This scheme ensures that we have the opportunity to clean up, undisturbed by any failed assertions. Be aware, however, that many members of the xUnit family do not call tearDown if the setUp method raises an error. When to Use It We can use Implicit Teardown whenever several tests with the same resources need to be destroyed or freed explicitly after the test has been completed and those resources will not be destroyed or freed automatically. We may discover this require- ment because we have Unrepeatable Tests (see Erratic Test on page 228) or Slow Tests (page 253) caused by the accumulation of detritus from many test runs. If the objects created by the test are internal resources and subject to automated memory management, then Garbage-Collected Teardown may eliminate a lot of work for us. If each test has a completely different set of objects to tear down, then In-line Teardown (page 509) may be more appropriate. In many cases, we can completely avoid manually written teardown logic by using Automated Tear- down (page 503). Implementation Notes The teardown logic in the tearDown method is most often created by refactoring from tests that had In-line Teardown. The tearDown method may need to be “fl exible” or “accommodating” for several reasons: • When a test fails or when a test error occurs, the Test Method may not have created all the fi xture objects. • If all the Test Methods in the Testcase Class (page 373) don’t use identical fi xtures, 1 there may be different sets of objects to clean up for different tests. Variation: Teardown Guard Clause We can avoid arbitrarily Conditional Test Logic (page 200) if we deal with the case where only a subset of the objects to be torn down are actually present by putting a guard clause (a simple if statement) around each teardown operation 1 That is, they augment the Implicit Teardown with some additional In-line Setup (page 408) or Delegated Setup (page 411). Implicit Teardown Implicit Teardown Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... Objects) Hard-Coded Test Doubles are typically hand-built by the test automater They come in several forms, including the Self Shunt (see Hard-Coded Test Double), where the Testcase Class (page 373 ) acts as the Test Double; the Anonymous Inner Test Double (see Hard-Coded Test Double), where language features are used to create the Test Double inside the Test Method (page 348); and the Test Double implemented... separate Test Double Class (see Hard-Coded Test Double) Each of these options is discussed in more detail in Hard-Coded Test Double Test Double 528 Chapter 23 Test Double Patterns Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Variation: Configurable Test Double When we want to use the same Test Double implementation in many tests, we will typically prefer to use a Configurable Test. .. Dummy Object Test Stub Test Spy Configurable Test Double Mock Object Fake Object Hard-Coded Test Double Figure 23.2 Types of Test Doubles with implementation choices Only Test Stubs, Test Spies, and Mock Objects need to be hard-coded or configured by the test Dummy Objects have no implementation; Fake Objects are installed but not controlled by the test Variation: Unconfigurable Test Doubles Neither Dummy... 558 Hard-Coded Test Double 568 Test- Specific Subclass 579 521 Test Double Patterns 522 Chapter 23 Test Double Patterns Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 3 Test Double How can we verify logic independently when code it depends on is unusable? How can we avoid Slow Tests? Also known as:... (see Hard-Coded Test Double on page 568) In this sense, a Dummy Object isn’t really a Test Double per se but rather an alternative to the value patterns Literal Value (page 71 4), Derived Value (page 71 8), and Generated Value (page 72 3) Variation: Procedural Test Stub A Test Double implemented in a procedural programming language is often called a test stub,” but I prefer to call it a Procedural Test Stub... Test Stub A Hard-Coded Test Stub has its responses hard-coded within its program logic These Test Stubs tend to be purpose-built for a single test or a very small number of tests See Hard-Coded Test Double (page 568) for more information Test Stub 532 Chapter 23 Test Double Patterns Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Variation: Configurable Test Stub When we want... building a different Hard-Coded Test Stub for each test, we can use a Configurable Test Stub (see Configurable Test Double on page 558) A test configures the Configurable Test Stub as part of its fixture setup phase Many members of the xUnit family offer tools with which to generate Configurable Test Doubles (page 558), including Configurable Test Stubs Motivating Example The following test verifies the basic... outputs to existing tests using a Replace Dependency with Test Double (page 522) refactoring It involves adding code to the fixture setup logic of the tests to create the Test Spy, configuring the Test Spy with any values it needs to return, and installing it At the end of the test, we add assertions comparing the expected method names and arguments of the Test Spy 542 Chapter 23 Test Double Patterns Simpo... replace Therefore, neither the test nor the test automater will need to configure “canned” responses or expectations; we just install the Test Double and let the SUT use it as if it were real Variation: Hard-Coded Test Double When we plan to use a specific Test Double in only a single test, it is often simplest to just hard -code the Test Double to return specific values (for Test Stubs) or expect specific... building Test Doubles are pretty much independent of their behavior (e.g., they apply to both Test Stubs and Mock Objects), I’ve chosen to split out the descriptions of the various ways we can build Hard-Coded Test Doubles and Configurable Test Doubles (page 558) into separate patterns Test Double 5 27 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Test Double Dummy Object Test . Double Test Double Mock Object Test Spy Dummy Object Test Stub Fake Object Hard-Coded Test Double Configurable Test Double Test Double Mock Object Test Spy Dummy Object Test Stub Fake Object Hard-Coded Test Double Test Double Test Double Simpo. Temporary Test Stub (see Test Stub) to avoid confusion. Test Double Mock Object Test Spy Dummy Object Test Stub Fake Object Test Double Mock Object Test Spy Dummy Object Test Stub Fake Object Test. separate Test Double Class (see Hard-Coded Test Double). Each of these options is discussed in more detail in Hard-Coded Test Double. Configurable Test Double Test Double Mock Object Test Spy Dummy Object Test Stub Fake Object Hard-Coded Test