Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Sustainable
                   Test-Driven
                  Development
                   Steve Freeman and Nat Pryce




                                   www.growing-object-oriented-software.com 2011




                           Out Now




Friday, 9 September 2011
Why Sustainability
                                                                          Matters (a)
        public class ExchangeRateUploaderTest extends EasyMockTestCase {
           private Logger logger;
           private CurrencyManager mockCurrencyManager;
           private ExchangeRateManager mockExchangeRateManager;
           private PriceManagerFactory mockPriceManagerFactory;
           private PriceManager mockPriceManager;
           private GodObject mockGod;
           private DatabaseFacade mockPersistenceManager;
           private DatabaseFacade mockFrameworkPersistenceManager;
           private CyclicProcessManager mockCyclicProcessManager;
           private SystemVariableManager mockSystemVariableManager;
           private ScreenManager mockScreenManager;
           private Registry registry;
           private User adminUser;
           private Server server;

           private ExchangeRateUploader newExchangeRateUploader(CyclicProcessThread thread) {
               return new ExchangeRateUploader(thread) {
                    @Override protected void initializeAction() throws FrameworkException {
                        // Does nothing to prevent excessive mocking
                     }
                     @Override    public Logger getLogger() { return logger; }
                     @Override    protected void setLogMDC() { }
                     @Override    protected User getUser() { return adminUser; }
                     @Override    protected CurrencyManager newCurrencyManager() { return mockCurrencyManager; }
                     @Override    protected PriceManagerFactory newPriceManagerFactory() { return mockPriceManagerFactory; }
                     @Override    protected CyclicProcessManager newCyclicProcessManager() { return mockCyclicProcessManager; }
                     @Override    protected DatabaseFacade newPersistenceManager() { return mockPersistenceManager; }
                     @Override    protected Registry newTaskPerformanceRegistry() { return registry; }
                     @Override    public PriceDataManager getPriceDataManager() { return null; }
                     @Override    protected ExchangeRateManager newExchangeRateManager() { return mockExchangeRateManager; }
               };

           }




                                                                                                      www.growing-object-oriented-software.com 2011




                                                                      Why Sustainability
           public void testInternalAction() throws FrameworkException {
               mockGod = addMock(GodObject.class);
               expect(mockGod.getPriceDataManager()).andStubReturn(null);
               mockPersistenceManager = addMock(DatabaseFacade.class);
               registry = addMock(Registry.class);
                                                                            Matters (b)
               adminUser = new TestUser("Admin", "", "", new TestCompany("company"), "");
               mockCyclicProcessManager();

               registry.finalizeThisThread(isA(String.class), isA(String.class));
               Date now = DateUtils.trimMinutesAndSecondsFromDate(new Date());

               mockSystemVariableManager();
               mockLogger();
               mockContextPersistenceManager();
               mockPriceManager();

               CyclicProcessThread thread = mockUserStateAndGetCyclicProcessThread();

               String primeName = "prime";
               String aName = "a";
               String otherName = "other";

               Currency primeCurrency = new TestCurrency(primeName, true);
               Currency aCurrency = new TestCurrency(aName, true);
               Currency otherCurrency = new TestCurrency(otherName, false);

               FXCurrencyPair aCurrencyPair = new FXCurrencyPair(primeCurrency, aCurrency);
               FXCurrencyPair otherCurrencyPair = new FXCurrencyPair(otherCurrency, primeCurrency);

               setupCurrencyManager(primeCurrency, aCurrency, otherCurrency);
               mockExchangeRateManager = addMock(ExchangeRateManager.class);

               mockGetFXRatesAtDatesForCurrencies(now, aCurrencyPair, otherCurrencyPair);

               FrameworkNumber aCurrencyValue = new FrameworkNumber("5");
               FrameworkNumber otherCurrencyValue = new FrameworkNumber("2");
               ExchangeRate aCurrencyRate = new ExchangeRate(primeCurrency, aCurrency, aCurrencyValue, now);
               ExchangeRate otherCurrencyRate = new ExchangeRate(otherCurrency, primeCurrency, otherCurrencyValue, now);
               expect(mockCurrencyManager.getParentToFractionalCurrencyMapForFractionalCurrency()).andStubReturn(newMap());

               expect(
                   mockExchangeRateManager.saveExchangeRate(null, new FrameworkString(primeName), new FrameworkString(aName), aCurrencyValue,
                       new FrameworkDate(now))).andReturn(null);
               expect(
                   mockExchangeRateManager.saveExchangeRate(null, new FrameworkString(otherName), new FrameworkString(primeName),
                       otherCurrencyValue, new FrameworkDate(now))).andReturn(null);

               Map<String, ExchangeRate> out = new HashMap<String, ExchangeRate>();
               out.put("primea", aCurrencyRate);
               out.put("otherprime", otherCurrencyRate);
               expect(mockPriceManager.getLatestExchangeRates(newList(aCurrencyPair, otherCurrencyPair))).andReturn(out);

               mockPMFactoryCleanup();

               replayMocks();

               ExchangeRateUploader uploader = newExchangeRateUploader(thread);
               uploader.initialise();
               uploader.run();
           }
                                                                                                      www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Why Sustainability
         private void mockPMFactoryCleanup() {
                                                                    Matters (c)
                 PersistenceFactory mockPersistenceFactory = addMock(PersistenceFactory.class);
                 mockPersistenceFactory.purgeAllStateForThisThread();
                 expect(mockGod.getPersistenceFactory()).andReturn(mockPersistenceFactory).anyTimes();
                 expect(mockPersistenceFactory.getExceptionsInRequest()).andReturn(Collections.<Throwable>emptyList()).times(1);
         }

         private void mockCyclicProcessManager() throws CyclicProcessException {
             mockCyclicProcessManager = addMock(CyclicProcessManager.class);
                 expect(mockGod.getCyclicProcessManager()).andStubReturn(mockCyclicProcessManager);
                 mockCyclicProcessManager.updateServerCyclicProcessCurrentRunStatus(isA(String.class),
                     isA(LISTENER_STATUS.class), isA(String.class), isA(Double.class), isA(Date.class));
                 expectLastCall().anyTimes();
                 server = addMock(Server.class);
                 expect(mockCyclicProcessManager.getThisServer()).andStubReturn(server);
         }

         private void setupCurrencyManager(Currency primeCurrency, Currency aCurrency, Currency otherCurrency) {
             mockCurrencyManager = addMock(CurrencyManager.class);
             List<Currency> allCurrencies = new ArrayList<Currency>();
                 allCurrencies.add(aCurrency);
                 allCurrencies.add(primeCurrency);
                 allCurrencies.add(otherCurrency);
                 expect(mockCurrencyManager.getPrimeCurrency()).andReturn(primeCurrency).anyTimes();
                 expect(mockCurrencyManager.getAllParentCurrencies()).andReturn(allCurrencies).times(2);
         }

         private void mockGetFXRatesAtDatesForCurrencies(Date now, FXCurrencyPair aCurrencyPair,
             FXCurrencyPair otherCurrencyPair) throws CurrencyException
         {
                 FrameworkNumber originalACurrencyRate = new FrameworkNumber("1.23");
                 Map<FXCurrencyPair, Collection<Date>> currencyPairAndDatesMap = new HashMap<FXCurrencyPair, Collection<Date>>();
                 currencyPairAndDatesMap.put(aCurrencyPair, Arrays.asList(now));
                 currencyPairAndDatesMap.put(otherCurrencyPair, Arrays.asList(now));
                 FXCurrencyPairRates outputObj = addMock(FXCurrencyPairRates.class);
                 expect(outputObj.rateMapSize()).andReturn(5).anyTimes();
                 expect(outputObj.getActualPriceDateForCurrencyPair(aCurrencyPair, now)).andReturn(null).once();
                 expect(outputObj.getRateFromFxRateMap(now, aCurrencyPair)).andReturn(originalACurrencyRate).once();
                 expect(outputObj.getActualPriceDateForCurrencyPair(otherCurrencyPair, now)).andReturn(null).once();
                 expect(outputObj.getRateFromFxRateMap(now, otherCurrencyPair)).andReturn(originalACurrencyRate);
                 expect(mockExchangeRateManager.getFXRatesAtDatesForCurrencies(currencyPairAndDatesMap)).andReturn(outputObj);
         }


                                                                                 www.growing-object-oriented-software.com 2011




                                                              Why Sustainability
                     Role mockAdminRole = addMock(Role.class);
                                                                    Matters (d)
                 private CyclicProcessThread mockUserStateAndGetCyclicProcessThread() {

                     CyclicProcessThread thread = addMock(CyclicProcessThread.class);
                     expect(thread.getAdminRole()).andReturn(mockAdminRole).anyTimes();
                     expect(thread.getAdminUser()).andReturn(adminUser).anyTimes();
                     thread.interrupt();
                     expectLastCall();
                     mockScreenManager = addMock(ScreenManager.class);
                     expect(mockGod.getScreenManager()).andReturn(mockScreenManager).anyTimes();
                     mockScreenManager.setThreadSignedOnState(new SignedOnState(adminUser, mockAdminRole, false));
                     expectLastCall().anyTimes();
                     expect(thread.getGod()).andReturn(mockGod).anyTimes();
                     expect(thread.getShutdownInProgress()).andReturn(false).anyTimes();
                     return thread;
                 }

                 private void mockContextPersistenceManager() {
                     mockFrameworkPersistenceManager = addMock(DatabaseFacade.class);
                     expect(mockGod.getDatabaseFacade()).andReturn(mockFrameworkPersistenceManager).anyTimes();
                     mockFrameworkPersistenceManager.beginNewSession();
                     expectLastCall().anyTimes();
                 }

                 private void mockPriceManager() throws PriceException {
                     mockPriceManagerFactory = addMock(PriceManagerFactory.class);
                     mockPriceManager = addMock(PriceManager.class);
                     expect(mockPriceManagerFactory.newPriceManager(mockFrameworkPersistenceManager,
                                                                     mockSystemVariableManager, null))
                         .andReturn(mockPriceManager).once();
                 }

                 private void mockSystemVariableManager() {
                     mockSystemVariableManager = addMock(SystemVariableManager.class);
                     expect(mockGod.getSystemVariableManager()).andReturn(mockSystemVariableManager).anyTimes();
                     expect(mockSystemVariableManager.getSystemVariable(CYCLIC_PROCESS_LISTENER_HEART_BEAT_TOLERANCE, "30000"))
                         .andReturn("30000").anyTimes();
                 }

                 private void mockLogger() {
                     logger = addMock(Logger.class);
                     logger.info(isA(String.class)); expectLastCall().atLeastOnce();
                     logger.debug(isA(String.class)); expectLastCall().atLeastOnce();
                 }
             }


                                                                                 www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Test Readability
            To design is to communicate clearly by
            whatever means you can control or
            master.
                                        —Milton Glaser




Friday, 9 September 2011
Test Names
                 Describe Features
          public class TargetObjectTest {
            @Test public void isReady() {



                                                           ✘
            @Test public void choose() {
            @Test public void choose1() {


       public class TargetObject {
         public void isReady() {
         public void choose(Picker picker) {



                               www.growing-object-oriented-software.com 2011




                    Test Names
                 Describe Features

 public class ListTests {
   @Test public void
   holdsItemsInTheOrderTheyWereAdded() {
   @Test public void
   canHoldMultipleReferencesToTheSameItem() {
   @Test public void
   throwsAnExceptionWhenRemovingAnItemItDoesntHold() {




                               www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Canonical Test Structure

public class StringTemplateTest {
 @Test public void expandsMacrosSurroundedWithBraces() {
    StringTemplate template = new StringTemplate("{a}{b}"); Setup
    HashMap<String,Object> macros = new HashMap<String,Object>();
    macros.put("a", "A"); macros.put("b", "B");

        String expanded = template.expand(macros);                   Execute

        assertThat(expanded, equalTo("AB"));                         Assert
                                                                     Teardown
    }
}




                                          www.growing-object-oriented-software.com 2011




           Streamline the Test Code


assertThat(instruments,
           hasItem(instrumentWithPrice(greaterThan(81))));




                                          www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Narrow Assertions and
                Expectations


 oneOf(failureReporter).cannotTranslateMessage(
                          with(SNIPER_ID), with(badMessage),
                          with(any(RuntimeException.class)));




                                      www.growing-object-oriented-software.com 2011




         Self-Describing Variables


         final static Chat UNUSED_CHAT = null;


           final static int INVALID_ID = 666;




                                      www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
@RunWith(NestedJUnit4.class)
 public class RemovingFullRowsTest extends Assert {
   private static final RotatableGrid PIECE =
     new RotatablePiece(
       "" +
       ".X.n" +
       ".X.n" +
       ".X.n");

   private final RowRemovalListener listener =
                             mock(RowRemovalListener.class);
   private Board board;

   private void dropAndFallToBottom(RotatableGrid piece) {
    board.drop(piece);
     while (board.hasFalling()) {
      board.tick();
     }
   }
   [...]

                           http://www.cs.helsinki.fi/u/luontola/tdd-2009/harjoitukset




public class When_many_rows_become_full_at_the_same_time {
  @Before
  public void dropPiece() {
     board = new Board("" +
                       "........n" +
                       "........n" +
                       "AAAA.AAAn" +
                       "BBBB..BBn" +
                       "CCCC.CCCn");
     board.addRowRemovalListener(listener);
     dropAndFallToBottom(PIECE);
   }

   @Test
   public void all_of_those_rows_are_removed() {
     String s = board.toString();
     assertFalse("Should not contain 'A':n" + s, s.contains("A"));
     assertFalse("Should not contain 'C':n" + s, s.contains("C"));
   }

   @Test
   public void the_row_removal_listener_is_notified_about_the_removed_rows() {
     verify(listener).onRowsRemoved(2);
   }
}




Friday, 9 September 2011
Constructing
         Complex Test Data
             Many attempts to communicate are
             nullified by saying too much.
                                                  —Robert Greenleaf




                  The Problem With
                  Object Structures
            Order order = new Order(
              new Customer("Sherlock Holmes",
                new Address("221b Baker Street",
                            "London",
                            new PostCode("NW1", "3RX"))));
            order.addLine(new OrderLine("Deerstalker Hat", 1));
            order.addLine(new OrderLine("Tweed Cape", 1));




 Order order1 = ExampleOrders.newDeerstalkerAndCapeAndSwordstickOrder();
 Order order2 = ExampleOrders.newDeerstalkerAndBootsOrder();




                                           www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Test Data Builder:
                     Add Indirection
             public class OrderBuilder {
               private Customer customer = new CustomerBuilder().build();
               private List<OrderLine> lines = new ArrayList<OrderLine>();
               private BigDecimal discountRate = BigDecimal.ZERO;

                 public OrderBuilder withCustomer(Customer customer) {
                   this.customer = customer;
                   return this;
                 }
                 public OrderBuilder withOrderLines(OrderLines lines) {
                   this.lines = lines;
                   return this;
                 }
                 public OrderBuilder withDiscount(BigDecimal discountRate) {
                   this.discountRate = discountRate;
                   return this;
                 }
                 public Order build() {
                   Order order = new Order(customer);
                   for (OrderLine line : lines) order.addLine(line);
                   order.setDiscountRate(discountRate);
                   return order;
                 }
             }
                                                   www.growing-object-oriented-software.com 2011




             Only Need To Include
               Relevant Values


  new OrderBuilder()
   .fromCustomer(
       new CustomerBuilder()
        .withAddress(new AddressBuilder().withNoPostcode().build())
        .build())
   .build();




                                                   www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Named Methods Make
              Mistakes Obvious


        new AddressBuilder()
         .withStreet("221b Baker Street")
         .withStreet2("London")
         .withPostCode("NW1 6XE")
         .build();




                                          www.growing-object-oriented-software.com 2011




               Use Builders to
            Create Similar Objects
            OrderBuilder hatAndCape = new OrderBuilder()
              .withLine("Deerstalker Hat", 1)
              .withLine("Tweed Cape", 1);




   Order orderWithDiscount    = hatAndCape
                                 .but().withDiscount(0.10).build();
   Order orderWithGiftVoucher = hatAndCape
                                 .but().withGiftVoucher("abc").build();




                                          www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Compacting Construction

     Order order = anOrder()
                    .from(aCustomer()
                           .with(anAddress().withNoPostcode()))
                    .build();



     Address aLongerAddress = anAddress()
                               .withStreet("221b Baker Street")
                               .withCity("London")
                               .with(postCode("NW1", "3RX"))
                               .build();




                                          www.growing-object-oriented-software.com 2011




               Refactor To Builders
 @Test public void reportsTotalSalesOfOrderedProducts() {
   sendAndProcess(anOrder()
                    .withLine("Deerstalker Hat", 1)
                    .withLine("Tweed Cape", 1));
   sendAndProcess(anOrder().withLine("Deerstalker Hat", 1));

    TotalSalesReport report = gui.openSalesReport();
    report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2)));
    report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1)));

 void sendAndProcess(OrderBuilder orderDetails) {
   Order order = orderDetails
                  .withDefaultCustomersReference(nextCustomerReference())
                  .build();
   requestSender.send(order);
   progressMonitor.waitForCompletion(order);
 }



                                          www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
What, Not How
      @Test public void reportsTotalSalesOfOrderedProducts() {
        havingReceived(anOrder()
                        .withLine("Deerstalker Hat", 1)
                        .withLine("Tweed Cape", 1));
        havingReceived(anOrder().withLine("Deerstalker Hat", 1));

          TotalSalesReport report = gui.openSalesReport();
          report.displaysTotalSalesFor("Deerstalker Hat", equalTo(2));
          report.displaysTotalSalesFor("Tweed Cape", equalTo(1));
      }




                                            www.growing-object-oriented-software.com 2011




              Test Diagnostics

              Mistakes are the portals of discovery.

                                                   —James Joyce




Friday, 9 September 2011
Explain Yourself

           assertEquals(16301, customer.getBalance());

           ComparisonFailure: expected:<[16301]> but was:<[16103]>




      assertEquals("balance", 16301, customer.getBalance());

      ComparisonFailure: balance expected:<[16301]> but was:<[16103]>




                                                    www.growing-object-oriented-software.com 2011




                    Describe Yourself
           ComparisonFailure: expected:<[a customer account id]>
                               but was:<[id not set]>

                                    java.lang.AssertionError: payment date
                                    Expected: <Thu Jan 01 01:00:01 GMT 1970>
                                         got: <Thu Jan 01 01:00:02 GMT 1970>


   Date startDate = namedDate(1000, "startDate");
   Date endDate = namedDate(2000, "endDate");

   Date namedDate(long timeValue, final String name) {
     return new Date(timeValue) {
      public String toString() { return name; }
     };
   }
                                    java.lang.AssertionError: payment date
                                    Expected: <startDate>
                                         got: <endDate>


                                                    www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Tracer Objects
    @RunWith(JMock.class) public class CustomerTest {
      final LineItem item1 = context.mock(LineItem.class, "item1");
      final LineItem item2 = context.mock(LineItem.class, "item2");
      final Billing billing = context.mock(Billing.class);

        @Test public void requestsInvoiceForPurchasedItems() {
          context.checking(new Expectations() {{
            oneOf(billing).add(item1);
            oneOf(billing).add(item2);
          }});
          customer.purchase(item1, item2);
          customer.requestInvoice(billing);
        }
    }
              not all expectations were satisfied
              expectations:
                expected once, already invoked 1 time: billing.add(<item1>)
                ! expected once, never invoked: billing.add(<item2>>)
              what happened before this:
                billing.add(<item1>)



                                                www.growing-object-oriented-software.com 2011




Friday, 9 September 2011
Test Flexibility
             Living plants are flexible and tender;
             the dead are brittle and dry.
             [...]
             The rigid and stiff will be broken.
             The soft and yielding will overcome.
                                    —Lao Tzu (c.604—531 B.C.)




               Specify Precisely
              What Should Happen
                 and No More




                                      www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Interlude




                   Information,
                Not Representation
        public interface CustomerBase {
          // Returns null if no customer found
          Customer findCustomerWithEmailAddress(String emailAddress);

      allowing(customerBase).findCustomerWithEmailAddress(theAddress);
                                              will(returnValue(null));


         public static final Customer NO_CUSTOMER_FOUND = null;


    public interface CustomerBase {
      Maybe<Customer> findCustomerWithEmailAddress(String emailAddress);
    }




                                          www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Precise Assertions
            assertThat(“strike price”,
                       92, equalTo(instrument.getStrikePrice()));



              assertThat(“transaction id”,
                         instrument.getTransactionId(),
                         largerThan(PREVIOUS_TRANSACTION_ID));



             assertThat(failureMessage,
                        allOf(containsString("strikePrice=92"),
                              containsString("id=FGD.430"),
                              containsString("is expired")));




                                           www.growing-object-oriented-software.com 2011




              Precise Expectations

   oneOf(auction).addAuctionEventListener(with(sniperForItem(itemId)));




 oneOf(auditTrail).recordFailure(with(
                                   allOf(containsString("strikePrice=92"),
                                         containsString("id=FGD.430"),
                                         containsString("is expired"))));




                                           www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Allow Queries
                 Expect Commands


       ignoring(auditTrail);
       allowing(catalog).getPriceForItem(item); will(returnValue(74));

       exactly(2).of(order).addItem(item, 74);




                                           www.growing-object-oriented-software.com 2011




                Only Enforce Order
                 When It Matters

@Test public void announcesMatchForOneAuction() {
  final AuctionSearcher auctionSearch =
                new AuctionSearcher(searchListener, asList(STUB_AUCTION1));

    context.checking(new Expectations() {{
      Sequence events = context.sequence("events");
      oneOf(searchListener).searchMatched(STUB_AUCTION1); inSequence(events);
      oneOf(searchListener).searchFinished();             inSequence(events);
     }});

    auctionSearch.searchFor(KEYWORDS);
}




                                           www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
“Guinea Pig” Objects
     public class XmlMarshallerTest {
       public static class MarshalledObject {
         private String privateField = "private";
         public transient String transientField = "transient";
         public final String publicFinalField = "public final";
         // constructors, accessors for private field, etc.
       }

         @Test public void marshallsAndUnmarshallsSerialisableFields() {
           XMLMarshaller marshaller = new XmlMarshaller();
           MarshalledObject original = new MarshalledObject();
           String xml = marshaller.marshall(original);
           MarshalledObject unmarshalled = marshaller.unmarshall(xml);
           assertThat(unmarshalled,
                      hasSameSerialisableFieldsAs(original));
         }
     }



                                            www.growing-object-oriented-software.com 2011




                 Tests Are Code Too


          • Expressiveness over convenience
          • Refactor and abstract
          • Focus on what matters
          • If it’s hard to test, that’s a clue




                                            www.growing-object-oriented-software.com 2011

Friday, 9 September 2011
Other sources




                               www.growing-object-oriented-software.com 2011




             And, of course…




Friday, 9 September 2011

More Related Content

What's hot

Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
Christoffer Noring
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
Tatsuya Maki
 
Vaadin today and tomorrow
Vaadin today and tomorrowVaadin today and tomorrow
Vaadin today and tomorrow
Joonas Lehtinen
 
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
탑크리에듀(구로디지털단지역3번출구 2분거리)
 
생산적인 개발을 위한 지속적인 테스트
생산적인 개발을 위한 지속적인 테스트생산적인 개발을 위한 지속적인 테스트
생산적인 개발을 위한 지속적인 테스트
기룡 남
 
The Ring programming language version 1.5.2 book - Part 67 of 181
The Ring programming language version 1.5.2 book - Part 67 of 181The Ring programming language version 1.5.2 book - Part 67 of 181
The Ring programming language version 1.5.2 book - Part 67 of 181
Mahmoud Samir Fayed
 
COScheduler In Depth
COScheduler In DepthCOScheduler In Depth
COScheduler In Depth
WO Community
 
The Ring programming language version 1.8 book - Part 105 of 202
The Ring programming language version 1.8 book - Part 105 of 202The Ring programming language version 1.8 book - Part 105 of 202
The Ring programming language version 1.8 book - Part 105 of 202
Mahmoud Samir Fayed
 
Advanced Hibernate V2
Advanced Hibernate V2Advanced Hibernate V2
Advanced Hibernate V2
Haitham Raik
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
Christoffer Noring
 
Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2
Martin Zapletal
 
Android development
Android developmentAndroid development
Android development
Gregoire BARRET
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGeneBang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
DroidConTLV
 
Vaadin 7
Vaadin 7Vaadin 7
Vaadin 7
Joonas Lehtinen
 
React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7
Dongho Cho
 
Vaadin7 modern-web-apps-in-java
Vaadin7 modern-web-apps-in-javaVaadin7 modern-web-apps-in-java
Vaadin7 modern-web-apps-in-java
Joonas Lehtinen
 
Testing microservices: Tools and Frameworks
Testing microservices: Tools and FrameworksTesting microservices: Tools and Frameworks
Testing microservices: Tools and Frameworks
Piotr Mińkowski
 
React lecture
React lectureReact lecture
React lecture
Christoffer Noring
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroid
GDG Korea
 
The Ring programming language version 1.5.4 book - Part 70 of 185
The Ring programming language version 1.5.4 book - Part 70 of 185The Ring programming language version 1.5.4 book - Part 70 of 185
The Ring programming language version 1.5.4 book - Part 70 of 185
Mahmoud Samir Fayed
 

What's hot (20)

Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
 
Vaadin today and tomorrow
Vaadin today and tomorrowVaadin today and tomorrow
Vaadin today and tomorrow
 
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
 
생산적인 개발을 위한 지속적인 테스트
생산적인 개발을 위한 지속적인 테스트생산적인 개발을 위한 지속적인 테스트
생산적인 개발을 위한 지속적인 테스트
 
The Ring programming language version 1.5.2 book - Part 67 of 181
The Ring programming language version 1.5.2 book - Part 67 of 181The Ring programming language version 1.5.2 book - Part 67 of 181
The Ring programming language version 1.5.2 book - Part 67 of 181
 
COScheduler In Depth
COScheduler In DepthCOScheduler In Depth
COScheduler In Depth
 
The Ring programming language version 1.8 book - Part 105 of 202
The Ring programming language version 1.8 book - Part 105 of 202The Ring programming language version 1.8 book - Part 105 of 202
The Ring programming language version 1.8 book - Part 105 of 202
 
Advanced Hibernate V2
Advanced Hibernate V2Advanced Hibernate V2
Advanced Hibernate V2
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
 
Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2
 
Android development
Android developmentAndroid development
Android development
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGeneBang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
 
Vaadin 7
Vaadin 7Vaadin 7
Vaadin 7
 
React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7
 
Vaadin7 modern-web-apps-in-java
Vaadin7 modern-web-apps-in-javaVaadin7 modern-web-apps-in-java
Vaadin7 modern-web-apps-in-java
 
Testing microservices: Tools and Frameworks
Testing microservices: Tools and FrameworksTesting microservices: Tools and Frameworks
Testing microservices: Tools and Frameworks
 
React lecture
React lectureReact lecture
React lecture
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroid
 
The Ring programming language version 1.5.4 book - Part 70 of 185
The Ring programming language version 1.5.4 book - Part 70 of 185The Ring programming language version 1.5.4 book - Part 70 of 185
The Ring programming language version 1.5.4 book - Part 70 of 185
 

Viewers also liked

Surfing the Agile Wave
Surfing the Agile WaveSurfing the Agile Wave
Surfing the Agile Wave
AgileOnTheBeach
 
Smart Metrics
Smart Metrics  Smart Metrics
Smart Metrics
AgileOnTheBeach
 
Research instruments case study
Research instruments case studyResearch instruments case study
Research instruments case study
AgileOnTheBeach
 
Sullivan cuff case study
Sullivan cuff case studySullivan cuff case study
Sullivan cuff case study
AgileOnTheBeach
 
The problem solvers problem
The problem solvers problemThe problem solvers problem
The problem solvers problem
AgileOnTheBeach
 
Slow and dirty with callouts
Slow and dirty with calloutsSlow and dirty with callouts
Slow and dirty with callouts
AgileOnTheBeach
 
System Error
System ErrorSystem Error
System Error
AgileOnTheBeach
 
Tool up your lamp stack
Tool up your lamp stackTool up your lamp stack
Tool up your lamp stack
AgileOnTheBeach
 
Value stream mapping
Value stream mapping  Value stream mapping
Value stream mapping
AgileOnTheBeach
 

Viewers also liked (9)

Surfing the Agile Wave
Surfing the Agile WaveSurfing the Agile Wave
Surfing the Agile Wave
 
Smart Metrics
Smart Metrics  Smart Metrics
Smart Metrics
 
Research instruments case study
Research instruments case studyResearch instruments case study
Research instruments case study
 
Sullivan cuff case study
Sullivan cuff case studySullivan cuff case study
Sullivan cuff case study
 
The problem solvers problem
The problem solvers problemThe problem solvers problem
The problem solvers problem
 
Slow and dirty with callouts
Slow and dirty with calloutsSlow and dirty with callouts
Slow and dirty with callouts
 
System Error
System ErrorSystem Error
System Error
 
Tool up your lamp stack
Tool up your lamp stackTool up your lamp stack
Tool up your lamp stack
 
Value stream mapping
Value stream mapping  Value stream mapping
Value stream mapping
 

Similar to Sustaining Test-Driven Development

Tdd thats-not-what-we-meant
Tdd thats-not-what-we-meantTdd thats-not-what-we-meant
Tdd thats-not-what-we-meant
AHAConference
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
John Ferguson Smart Limited
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
javatwo2011
 
Unit testing with mock libs
Unit testing with mock libsUnit testing with mock libs
Unit testing with mock libs
Valentin Kolesnikov
 
Jersey Guice AOP
Jersey Guice AOPJersey Guice AOP
Jersey Guice AOP
Domenico Briganti
 
Spring and Cloud Foundry; a Marriage Made in Heaven
Spring and Cloud Foundry; a Marriage Made in HeavenSpring and Cloud Foundry; a Marriage Made in Heaven
Spring and Cloud Foundry; a Marriage Made in Heaven
Joshua Long
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
Tomek Kaczanowski
 
Spring mvc my Faviourite Slide
Spring mvc my Faviourite SlideSpring mvc my Faviourite Slide
Spring mvc my Faviourite Slide
Daniel Adenew
 
Advance Java Programs skeleton
Advance Java Programs skeletonAdvance Java Programs skeleton
Advance Java Programs skeleton
Iram Ramrajkar
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutions
benewu
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradle
Thierry Wasylczenko
 
Vaadin7
Vaadin7Vaadin7
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
Ali Parmaksiz
 
droidQuery: The Android port of jQuery
droidQuery: The Android port of jQuerydroidQuery: The Android port of jQuery
droidQuery: The Android port of jQuery
PhDBrown
 
Test-driven Development with AEM
Test-driven Development with AEMTest-driven Development with AEM
Test-driven Development with AEM
Jan Wloka
 
Vaadin 7
Vaadin 7Vaadin 7
Vaadin 7
Joonas Lehtinen
 
Rhino Mocks
Rhino MocksRhino Mocks
Rhino Mocks
Anand Kumar Rajana
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3
Simon Su
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
Tomek Kaczanowski
 
Rx workshop
Rx workshopRx workshop
Rx workshop
Ryan Riley
 

Similar to Sustaining Test-Driven Development (20)

Tdd thats-not-what-we-meant
Tdd thats-not-what-we-meantTdd thats-not-what-we-meant
Tdd thats-not-what-we-meant
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Unit testing with mock libs
Unit testing with mock libsUnit testing with mock libs
Unit testing with mock libs
 
Jersey Guice AOP
Jersey Guice AOPJersey Guice AOP
Jersey Guice AOP
 
Spring and Cloud Foundry; a Marriage Made in Heaven
Spring and Cloud Foundry; a Marriage Made in HeavenSpring and Cloud Foundry; a Marriage Made in Heaven
Spring and Cloud Foundry; a Marriage Made in Heaven
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Spring mvc my Faviourite Slide
Spring mvc my Faviourite SlideSpring mvc my Faviourite Slide
Spring mvc my Faviourite Slide
 
Advance Java Programs skeleton
Advance Java Programs skeletonAdvance Java Programs skeleton
Advance Java Programs skeleton
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutions
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradle
 
Vaadin7
Vaadin7Vaadin7
Vaadin7
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
 
droidQuery: The Android port of jQuery
droidQuery: The Android port of jQuerydroidQuery: The Android port of jQuery
droidQuery: The Android port of jQuery
 
Test-driven Development with AEM
Test-driven Development with AEMTest-driven Development with AEM
Test-driven Development with AEM
 
Vaadin 7
Vaadin 7Vaadin 7
Vaadin 7
 
Rhino Mocks
Rhino MocksRhino Mocks
Rhino Mocks
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
Rx workshop
Rx workshopRx workshop
Rx workshop
 

More from AgileOnTheBeach

Research instruments case study
Research instruments case studyResearch instruments case study
Research instruments case study
AgileOnTheBeach
 
Objective agility
Objective agilityObjective agility
Objective agility
AgileOnTheBeach
 
Lean and lego
Lean and lego Lean and lego
Lean and lego
AgileOnTheBeach
 
Ignition team - creating agile companies
Ignition team - creating agile companiesIgnition team - creating agile companies
Ignition team - creating agile companies
AgileOnTheBeach
 
First build the right thing
First build the right thingFirst build the right thing
First build the right thing
AgileOnTheBeach
 
Embedded storycrafting
Embedded storycraftingEmbedded storycrafting
Embedded storycrafting
AgileOnTheBeach
 
Beware sharp tools
Beware sharp toolsBeware sharp tools
Beware sharp tools
AgileOnTheBeach
 
Lean startup
Lean startupLean startup
Lean startup
AgileOnTheBeach
 
Behaviour Driven Development - Beyond given when then
Behaviour Driven Development - Beyond given when thenBehaviour Driven Development - Beyond given when then
Behaviour Driven Development - Beyond given when then
AgileOnTheBeach
 
Agile in Practice
Agile in PracticeAgile in Practice
Agile in Practice
AgileOnTheBeach
 
Oxford Innovation - case study
Oxford Innovation - case studyOxford Innovation - case study
Oxford Innovation - case study
AgileOnTheBeach
 
Feedback Loops in Agile Development
Feedback Loops in Agile DevelopmentFeedback Loops in Agile Development
Feedback Loops in Agile Development
AgileOnTheBeach
 
A question of craftsmanship
A question of craftsmanshipA question of craftsmanship
A question of craftsmanship
AgileOnTheBeach
 
The problem solvers problem
The problem solvers problemThe problem solvers problem
The problem solvers problem
AgileOnTheBeach
 

More from AgileOnTheBeach (14)

Research instruments case study
Research instruments case studyResearch instruments case study
Research instruments case study
 
Objective agility
Objective agilityObjective agility
Objective agility
 
Lean and lego
Lean and lego Lean and lego
Lean and lego
 
Ignition team - creating agile companies
Ignition team - creating agile companiesIgnition team - creating agile companies
Ignition team - creating agile companies
 
First build the right thing
First build the right thingFirst build the right thing
First build the right thing
 
Embedded storycrafting
Embedded storycraftingEmbedded storycrafting
Embedded storycrafting
 
Beware sharp tools
Beware sharp toolsBeware sharp tools
Beware sharp tools
 
Lean startup
Lean startupLean startup
Lean startup
 
Behaviour Driven Development - Beyond given when then
Behaviour Driven Development - Beyond given when thenBehaviour Driven Development - Beyond given when then
Behaviour Driven Development - Beyond given when then
 
Agile in Practice
Agile in PracticeAgile in Practice
Agile in Practice
 
Oxford Innovation - case study
Oxford Innovation - case studyOxford Innovation - case study
Oxford Innovation - case study
 
Feedback Loops in Agile Development
Feedback Loops in Agile DevelopmentFeedback Loops in Agile Development
Feedback Loops in Agile Development
 
A question of craftsmanship
A question of craftsmanshipA question of craftsmanship
A question of craftsmanship
 
The problem solvers problem
The problem solvers problemThe problem solvers problem
The problem solvers problem
 

Recently uploaded

Global Collaboration for Space Exploration.pdf
Global Collaboration for Space Exploration.pdfGlobal Collaboration for Space Exploration.pdf
Global Collaboration for Space Exploration.pdf
Sachin Chitre
 
Jacquard Fabric Explained: Origins, Characteristics, and Uses
Jacquard Fabric Explained: Origins, Characteristics, and UsesJacquard Fabric Explained: Origins, Characteristics, and Uses
Jacquard Fabric Explained: Origins, Characteristics, and Uses
ldtexsolbl
 
TribeQonf2024_Dimpy_ShiftingSecurityLeft
TribeQonf2024_Dimpy_ShiftingSecurityLeftTribeQonf2024_Dimpy_ShiftingSecurityLeft
TribeQonf2024_Dimpy_ShiftingSecurityLeft
Dimpy Adhikary
 
The learners analyze the various sectors of ICT and evaluate the potential ca...
The learners analyze the various sectors of ICT and evaluate the potential ca...The learners analyze the various sectors of ICT and evaluate the potential ca...
The learners analyze the various sectors of ICT and evaluate the potential ca...
maricrismontales
 
Network Auto Configuration and Correction using Python.pptx
Network Auto Configuration and Correction using Python.pptxNetwork Auto Configuration and Correction using Python.pptx
Network Auto Configuration and Correction using Python.pptx
saikumaresh2
 
Top keywords searches on business in AUS
Top keywords searches on business in AUSTop keywords searches on business in AUS
Top keywords searches on business in AUS
riannecreativetwo
 
Leading Bigcommerce Development Services for Online Retailers
Leading Bigcommerce Development Services for Online RetailersLeading Bigcommerce Development Services for Online Retailers
Leading Bigcommerce Development Services for Online Retailers
SynapseIndia
 
CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2
CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2
CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2
wromeetup
 
SuratMeetup-MuleSoft + Salt Security for API Security.pptx
SuratMeetup-MuleSoft + Salt Security for API Security.pptxSuratMeetup-MuleSoft + Salt Security for API Security.pptx
SuratMeetup-MuleSoft + Salt Security for API Security.pptx
nitishjain2015
 
FIDO Munich Seminar Workforce Authentication Case Study.pptx
FIDO Munich Seminar Workforce Authentication Case Study.pptxFIDO Munich Seminar Workforce Authentication Case Study.pptx
FIDO Munich Seminar Workforce Authentication Case Study.pptx
FIDO Alliance
 
Informatika smk kelas 10 kurikulum merdeka.pptx
Informatika smk kelas 10 kurikulum merdeka.pptxInformatika smk kelas 10 kurikulum merdeka.pptx
Informatika smk kelas 10 kurikulum merdeka.pptx
OkyPrayudi
 
Starlink Product Specifications_HighPerformance-1.pdf
Starlink Product Specifications_HighPerformance-1.pdfStarlink Product Specifications_HighPerformance-1.pdf
Starlink Product Specifications_HighPerformance-1.pdf
ssuser0b9571
 
Multimodal Embeddings (continued) - South Bay Meetup Slides
Multimodal Embeddings (continued) - South Bay Meetup SlidesMultimodal Embeddings (continued) - South Bay Meetup Slides
Multimodal Embeddings (continued) - South Bay Meetup Slides
Zilliz
 
FIDO Munich Seminar: Securing Smart Car.pptx
FIDO Munich Seminar: Securing Smart Car.pptxFIDO Munich Seminar: Securing Smart Car.pptx
FIDO Munich Seminar: Securing Smart Car.pptx
FIDO Alliance
 
Understanding NFT Marketplace Ecosystem.pptx
Understanding  NFT Marketplace Ecosystem.pptxUnderstanding  NFT Marketplace Ecosystem.pptx
Understanding NFT Marketplace Ecosystem.pptx
NFT Space.
 
Bài tập tiếng anh lớp 9 - Ôn tập tuyển sinh
Bài tập tiếng anh lớp 9 - Ôn tập tuyển sinhBài tập tiếng anh lớp 9 - Ôn tập tuyển sinh
Bài tập tiếng anh lớp 9 - Ôn tập tuyển sinh
NguynThNhQunh59
 
How CXAI Toolkit uses RAG for Intelligent Q&A
How CXAI Toolkit uses RAG for Intelligent Q&AHow CXAI Toolkit uses RAG for Intelligent Q&A
How CXAI Toolkit uses RAG for Intelligent Q&A
Zilliz
 
Mega MUG 2024: Working smarter in Marketo
Mega MUG 2024: Working smarter in MarketoMega MUG 2024: Working smarter in Marketo
Mega MUG 2024: Working smarter in Marketo
Stephanie Tyagita
 
Best USA IPTV Providers to Stream in 2024.pdf
Best USA IPTV Providers to Stream in 2024.pdfBest USA IPTV Providers to Stream in 2024.pdf
Best USA IPTV Providers to Stream in 2024.pdf
perth Riya
 
AMD Zen 5 Architecture Deep Dive from Tech Day
AMD Zen 5 Architecture Deep Dive from Tech DayAMD Zen 5 Architecture Deep Dive from Tech Day
AMD Zen 5 Architecture Deep Dive from Tech Day
Low Hong Chuan
 

Recently uploaded (20)

Global Collaboration for Space Exploration.pdf
Global Collaboration for Space Exploration.pdfGlobal Collaboration for Space Exploration.pdf
Global Collaboration for Space Exploration.pdf
 
Jacquard Fabric Explained: Origins, Characteristics, and Uses
Jacquard Fabric Explained: Origins, Characteristics, and UsesJacquard Fabric Explained: Origins, Characteristics, and Uses
Jacquard Fabric Explained: Origins, Characteristics, and Uses
 
TribeQonf2024_Dimpy_ShiftingSecurityLeft
TribeQonf2024_Dimpy_ShiftingSecurityLeftTribeQonf2024_Dimpy_ShiftingSecurityLeft
TribeQonf2024_Dimpy_ShiftingSecurityLeft
 
The learners analyze the various sectors of ICT and evaluate the potential ca...
The learners analyze the various sectors of ICT and evaluate the potential ca...The learners analyze the various sectors of ICT and evaluate the potential ca...
The learners analyze the various sectors of ICT and evaluate the potential ca...
 
Network Auto Configuration and Correction using Python.pptx
Network Auto Configuration and Correction using Python.pptxNetwork Auto Configuration and Correction using Python.pptx
Network Auto Configuration and Correction using Python.pptx
 
Top keywords searches on business in AUS
Top keywords searches on business in AUSTop keywords searches on business in AUS
Top keywords searches on business in AUS
 
Leading Bigcommerce Development Services for Online Retailers
Leading Bigcommerce Development Services for Online RetailersLeading Bigcommerce Development Services for Online Retailers
Leading Bigcommerce Development Services for Online Retailers
 
CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2
CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2
CI/CD pipelines for CloudHub 2.0 - Wroclaw MuleSoft Meetup #2
 
SuratMeetup-MuleSoft + Salt Security for API Security.pptx
SuratMeetup-MuleSoft + Salt Security for API Security.pptxSuratMeetup-MuleSoft + Salt Security for API Security.pptx
SuratMeetup-MuleSoft + Salt Security for API Security.pptx
 
FIDO Munich Seminar Workforce Authentication Case Study.pptx
FIDO Munich Seminar Workforce Authentication Case Study.pptxFIDO Munich Seminar Workforce Authentication Case Study.pptx
FIDO Munich Seminar Workforce Authentication Case Study.pptx
 
Informatika smk kelas 10 kurikulum merdeka.pptx
Informatika smk kelas 10 kurikulum merdeka.pptxInformatika smk kelas 10 kurikulum merdeka.pptx
Informatika smk kelas 10 kurikulum merdeka.pptx
 
Starlink Product Specifications_HighPerformance-1.pdf
Starlink Product Specifications_HighPerformance-1.pdfStarlink Product Specifications_HighPerformance-1.pdf
Starlink Product Specifications_HighPerformance-1.pdf
 
Multimodal Embeddings (continued) - South Bay Meetup Slides
Multimodal Embeddings (continued) - South Bay Meetup SlidesMultimodal Embeddings (continued) - South Bay Meetup Slides
Multimodal Embeddings (continued) - South Bay Meetup Slides
 
FIDO Munich Seminar: Securing Smart Car.pptx
FIDO Munich Seminar: Securing Smart Car.pptxFIDO Munich Seminar: Securing Smart Car.pptx
FIDO Munich Seminar: Securing Smart Car.pptx
 
Understanding NFT Marketplace Ecosystem.pptx
Understanding  NFT Marketplace Ecosystem.pptxUnderstanding  NFT Marketplace Ecosystem.pptx
Understanding NFT Marketplace Ecosystem.pptx
 
Bài tập tiếng anh lớp 9 - Ôn tập tuyển sinh
Bài tập tiếng anh lớp 9 - Ôn tập tuyển sinhBài tập tiếng anh lớp 9 - Ôn tập tuyển sinh
Bài tập tiếng anh lớp 9 - Ôn tập tuyển sinh
 
How CXAI Toolkit uses RAG for Intelligent Q&A
How CXAI Toolkit uses RAG for Intelligent Q&AHow CXAI Toolkit uses RAG for Intelligent Q&A
How CXAI Toolkit uses RAG for Intelligent Q&A
 
Mega MUG 2024: Working smarter in Marketo
Mega MUG 2024: Working smarter in MarketoMega MUG 2024: Working smarter in Marketo
Mega MUG 2024: Working smarter in Marketo
 
Best USA IPTV Providers to Stream in 2024.pdf
Best USA IPTV Providers to Stream in 2024.pdfBest USA IPTV Providers to Stream in 2024.pdf
Best USA IPTV Providers to Stream in 2024.pdf
 
AMD Zen 5 Architecture Deep Dive from Tech Day
AMD Zen 5 Architecture Deep Dive from Tech DayAMD Zen 5 Architecture Deep Dive from Tech Day
AMD Zen 5 Architecture Deep Dive from Tech Day
 

Sustaining Test-Driven Development

  • 1. Sustainable Test-Driven Development Steve Freeman and Nat Pryce www.growing-object-oriented-software.com 2011 Out Now Friday, 9 September 2011
  • 2. Why Sustainability Matters (a) public class ExchangeRateUploaderTest extends EasyMockTestCase { private Logger logger; private CurrencyManager mockCurrencyManager; private ExchangeRateManager mockExchangeRateManager; private PriceManagerFactory mockPriceManagerFactory; private PriceManager mockPriceManager; private GodObject mockGod; private DatabaseFacade mockPersistenceManager; private DatabaseFacade mockFrameworkPersistenceManager; private CyclicProcessManager mockCyclicProcessManager; private SystemVariableManager mockSystemVariableManager; private ScreenManager mockScreenManager; private Registry registry; private User adminUser; private Server server; private ExchangeRateUploader newExchangeRateUploader(CyclicProcessThread thread) { return new ExchangeRateUploader(thread) { @Override protected void initializeAction() throws FrameworkException { // Does nothing to prevent excessive mocking } @Override public Logger getLogger() { return logger; } @Override protected void setLogMDC() { } @Override protected User getUser() { return adminUser; } @Override protected CurrencyManager newCurrencyManager() { return mockCurrencyManager; } @Override protected PriceManagerFactory newPriceManagerFactory() { return mockPriceManagerFactory; } @Override protected CyclicProcessManager newCyclicProcessManager() { return mockCyclicProcessManager; } @Override protected DatabaseFacade newPersistenceManager() { return mockPersistenceManager; } @Override protected Registry newTaskPerformanceRegistry() { return registry; } @Override public PriceDataManager getPriceDataManager() { return null; } @Override protected ExchangeRateManager newExchangeRateManager() { return mockExchangeRateManager; } }; } www.growing-object-oriented-software.com 2011 Why Sustainability public void testInternalAction() throws FrameworkException { mockGod = addMock(GodObject.class); expect(mockGod.getPriceDataManager()).andStubReturn(null); mockPersistenceManager = addMock(DatabaseFacade.class); registry = addMock(Registry.class); Matters (b) adminUser = new TestUser("Admin", "", "", new TestCompany("company"), ""); mockCyclicProcessManager(); registry.finalizeThisThread(isA(String.class), isA(String.class)); Date now = DateUtils.trimMinutesAndSecondsFromDate(new Date()); mockSystemVariableManager(); mockLogger(); mockContextPersistenceManager(); mockPriceManager(); CyclicProcessThread thread = mockUserStateAndGetCyclicProcessThread(); String primeName = "prime"; String aName = "a"; String otherName = "other"; Currency primeCurrency = new TestCurrency(primeName, true); Currency aCurrency = new TestCurrency(aName, true); Currency otherCurrency = new TestCurrency(otherName, false); FXCurrencyPair aCurrencyPair = new FXCurrencyPair(primeCurrency, aCurrency); FXCurrencyPair otherCurrencyPair = new FXCurrencyPair(otherCurrency, primeCurrency); setupCurrencyManager(primeCurrency, aCurrency, otherCurrency); mockExchangeRateManager = addMock(ExchangeRateManager.class); mockGetFXRatesAtDatesForCurrencies(now, aCurrencyPair, otherCurrencyPair); FrameworkNumber aCurrencyValue = new FrameworkNumber("5"); FrameworkNumber otherCurrencyValue = new FrameworkNumber("2"); ExchangeRate aCurrencyRate = new ExchangeRate(primeCurrency, aCurrency, aCurrencyValue, now); ExchangeRate otherCurrencyRate = new ExchangeRate(otherCurrency, primeCurrency, otherCurrencyValue, now); expect(mockCurrencyManager.getParentToFractionalCurrencyMapForFractionalCurrency()).andStubReturn(newMap()); expect( mockExchangeRateManager.saveExchangeRate(null, new FrameworkString(primeName), new FrameworkString(aName), aCurrencyValue, new FrameworkDate(now))).andReturn(null); expect( mockExchangeRateManager.saveExchangeRate(null, new FrameworkString(otherName), new FrameworkString(primeName), otherCurrencyValue, new FrameworkDate(now))).andReturn(null); Map<String, ExchangeRate> out = new HashMap<String, ExchangeRate>(); out.put("primea", aCurrencyRate); out.put("otherprime", otherCurrencyRate); expect(mockPriceManager.getLatestExchangeRates(newList(aCurrencyPair, otherCurrencyPair))).andReturn(out); mockPMFactoryCleanup(); replayMocks(); ExchangeRateUploader uploader = newExchangeRateUploader(thread); uploader.initialise(); uploader.run(); } www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 3. Why Sustainability private void mockPMFactoryCleanup() { Matters (c) PersistenceFactory mockPersistenceFactory = addMock(PersistenceFactory.class); mockPersistenceFactory.purgeAllStateForThisThread(); expect(mockGod.getPersistenceFactory()).andReturn(mockPersistenceFactory).anyTimes(); expect(mockPersistenceFactory.getExceptionsInRequest()).andReturn(Collections.<Throwable>emptyList()).times(1); } private void mockCyclicProcessManager() throws CyclicProcessException { mockCyclicProcessManager = addMock(CyclicProcessManager.class); expect(mockGod.getCyclicProcessManager()).andStubReturn(mockCyclicProcessManager); mockCyclicProcessManager.updateServerCyclicProcessCurrentRunStatus(isA(String.class), isA(LISTENER_STATUS.class), isA(String.class), isA(Double.class), isA(Date.class)); expectLastCall().anyTimes(); server = addMock(Server.class); expect(mockCyclicProcessManager.getThisServer()).andStubReturn(server); } private void setupCurrencyManager(Currency primeCurrency, Currency aCurrency, Currency otherCurrency) { mockCurrencyManager = addMock(CurrencyManager.class); List<Currency> allCurrencies = new ArrayList<Currency>(); allCurrencies.add(aCurrency); allCurrencies.add(primeCurrency); allCurrencies.add(otherCurrency); expect(mockCurrencyManager.getPrimeCurrency()).andReturn(primeCurrency).anyTimes(); expect(mockCurrencyManager.getAllParentCurrencies()).andReturn(allCurrencies).times(2); } private void mockGetFXRatesAtDatesForCurrencies(Date now, FXCurrencyPair aCurrencyPair, FXCurrencyPair otherCurrencyPair) throws CurrencyException { FrameworkNumber originalACurrencyRate = new FrameworkNumber("1.23"); Map<FXCurrencyPair, Collection<Date>> currencyPairAndDatesMap = new HashMap<FXCurrencyPair, Collection<Date>>(); currencyPairAndDatesMap.put(aCurrencyPair, Arrays.asList(now)); currencyPairAndDatesMap.put(otherCurrencyPair, Arrays.asList(now)); FXCurrencyPairRates outputObj = addMock(FXCurrencyPairRates.class); expect(outputObj.rateMapSize()).andReturn(5).anyTimes(); expect(outputObj.getActualPriceDateForCurrencyPair(aCurrencyPair, now)).andReturn(null).once(); expect(outputObj.getRateFromFxRateMap(now, aCurrencyPair)).andReturn(originalACurrencyRate).once(); expect(outputObj.getActualPriceDateForCurrencyPair(otherCurrencyPair, now)).andReturn(null).once(); expect(outputObj.getRateFromFxRateMap(now, otherCurrencyPair)).andReturn(originalACurrencyRate); expect(mockExchangeRateManager.getFXRatesAtDatesForCurrencies(currencyPairAndDatesMap)).andReturn(outputObj); } www.growing-object-oriented-software.com 2011 Why Sustainability Role mockAdminRole = addMock(Role.class); Matters (d) private CyclicProcessThread mockUserStateAndGetCyclicProcessThread() { CyclicProcessThread thread = addMock(CyclicProcessThread.class); expect(thread.getAdminRole()).andReturn(mockAdminRole).anyTimes(); expect(thread.getAdminUser()).andReturn(adminUser).anyTimes(); thread.interrupt(); expectLastCall(); mockScreenManager = addMock(ScreenManager.class); expect(mockGod.getScreenManager()).andReturn(mockScreenManager).anyTimes(); mockScreenManager.setThreadSignedOnState(new SignedOnState(adminUser, mockAdminRole, false)); expectLastCall().anyTimes(); expect(thread.getGod()).andReturn(mockGod).anyTimes(); expect(thread.getShutdownInProgress()).andReturn(false).anyTimes(); return thread; } private void mockContextPersistenceManager() { mockFrameworkPersistenceManager = addMock(DatabaseFacade.class); expect(mockGod.getDatabaseFacade()).andReturn(mockFrameworkPersistenceManager).anyTimes(); mockFrameworkPersistenceManager.beginNewSession(); expectLastCall().anyTimes(); } private void mockPriceManager() throws PriceException { mockPriceManagerFactory = addMock(PriceManagerFactory.class); mockPriceManager = addMock(PriceManager.class); expect(mockPriceManagerFactory.newPriceManager(mockFrameworkPersistenceManager, mockSystemVariableManager, null)) .andReturn(mockPriceManager).once(); } private void mockSystemVariableManager() { mockSystemVariableManager = addMock(SystemVariableManager.class); expect(mockGod.getSystemVariableManager()).andReturn(mockSystemVariableManager).anyTimes(); expect(mockSystemVariableManager.getSystemVariable(CYCLIC_PROCESS_LISTENER_HEART_BEAT_TOLERANCE, "30000")) .andReturn("30000").anyTimes(); } private void mockLogger() { logger = addMock(Logger.class); logger.info(isA(String.class)); expectLastCall().atLeastOnce(); logger.debug(isA(String.class)); expectLastCall().atLeastOnce(); } } www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 4. Test Readability To design is to communicate clearly by whatever means you can control or master. —Milton Glaser Friday, 9 September 2011
  • 5. Test Names Describe Features public class TargetObjectTest { @Test public void isReady() { ✘ @Test public void choose() { @Test public void choose1() { public class TargetObject { public void isReady() { public void choose(Picker picker) { www.growing-object-oriented-software.com 2011 Test Names Describe Features public class ListTests { @Test public void holdsItemsInTheOrderTheyWereAdded() { @Test public void canHoldMultipleReferencesToTheSameItem() { @Test public void throwsAnExceptionWhenRemovingAnItemItDoesntHold() { www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 6. Canonical Test Structure public class StringTemplateTest { @Test public void expandsMacrosSurroundedWithBraces() { StringTemplate template = new StringTemplate("{a}{b}"); Setup HashMap<String,Object> macros = new HashMap<String,Object>(); macros.put("a", "A"); macros.put("b", "B"); String expanded = template.expand(macros); Execute assertThat(expanded, equalTo("AB")); Assert Teardown } } www.growing-object-oriented-software.com 2011 Streamline the Test Code assertThat(instruments, hasItem(instrumentWithPrice(greaterThan(81)))); www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 7. Narrow Assertions and Expectations oneOf(failureReporter).cannotTranslateMessage( with(SNIPER_ID), with(badMessage), with(any(RuntimeException.class))); www.growing-object-oriented-software.com 2011 Self-Describing Variables final static Chat UNUSED_CHAT = null; final static int INVALID_ID = 666; www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 8. @RunWith(NestedJUnit4.class) public class RemovingFullRowsTest extends Assert { private static final RotatableGrid PIECE = new RotatablePiece( "" + ".X.n" + ".X.n" + ".X.n");   private final RowRemovalListener listener = mock(RowRemovalListener.class);   private Board board;   private void dropAndFallToBottom(RotatableGrid piece) {    board.drop(piece);     while (board.hasFalling()) {      board.tick();     }   } [...] http://www.cs.helsinki.fi/u/luontola/tdd-2009/harjoitukset public class When_many_rows_become_full_at_the_same_time { @Before public void dropPiece() { board = new Board("" + "........n" + "........n" + "AAAA.AAAn" + "BBBB..BBn" + "CCCC.CCCn"); board.addRowRemovalListener(listener); dropAndFallToBottom(PIECE); } @Test public void all_of_those_rows_are_removed() { String s = board.toString(); assertFalse("Should not contain 'A':n" + s, s.contains("A")); assertFalse("Should not contain 'C':n" + s, s.contains("C")); }    @Test public void the_row_removal_listener_is_notified_about_the_removed_rows() { verify(listener).onRowsRemoved(2);    } } Friday, 9 September 2011
  • 9. Constructing Complex Test Data Many attempts to communicate are nullified by saying too much. —Robert Greenleaf The Problem With Object Structures Order order = new Order( new Customer("Sherlock Holmes", new Address("221b Baker Street", "London", new PostCode("NW1", "3RX")))); order.addLine(new OrderLine("Deerstalker Hat", 1)); order.addLine(new OrderLine("Tweed Cape", 1)); Order order1 = ExampleOrders.newDeerstalkerAndCapeAndSwordstickOrder(); Order order2 = ExampleOrders.newDeerstalkerAndBootsOrder(); www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 10. Test Data Builder: Add Indirection public class OrderBuilder { private Customer customer = new CustomerBuilder().build(); private List<OrderLine> lines = new ArrayList<OrderLine>(); private BigDecimal discountRate = BigDecimal.ZERO; public OrderBuilder withCustomer(Customer customer) { this.customer = customer; return this; } public OrderBuilder withOrderLines(OrderLines lines) { this.lines = lines; return this; } public OrderBuilder withDiscount(BigDecimal discountRate) { this.discountRate = discountRate; return this; } public Order build() { Order order = new Order(customer); for (OrderLine line : lines) order.addLine(line); order.setDiscountRate(discountRate); return order; } } www.growing-object-oriented-software.com 2011 Only Need To Include Relevant Values new OrderBuilder() .fromCustomer( new CustomerBuilder() .withAddress(new AddressBuilder().withNoPostcode().build()) .build()) .build(); www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 11. Named Methods Make Mistakes Obvious new AddressBuilder() .withStreet("221b Baker Street") .withStreet2("London") .withPostCode("NW1 6XE") .build(); www.growing-object-oriented-software.com 2011 Use Builders to Create Similar Objects OrderBuilder hatAndCape = new OrderBuilder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1); Order orderWithDiscount = hatAndCape .but().withDiscount(0.10).build(); Order orderWithGiftVoucher = hatAndCape .but().withGiftVoucher("abc").build(); www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 12. Compacting Construction Order order = anOrder() .from(aCustomer() .with(anAddress().withNoPostcode())) .build(); Address aLongerAddress = anAddress() .withStreet("221b Baker Street") .withCity("London") .with(postCode("NW1", "3RX")) .build(); www.growing-object-oriented-software.com 2011 Refactor To Builders @Test public void reportsTotalSalesOfOrderedProducts() { sendAndProcess(anOrder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1)); sendAndProcess(anOrder().withLine("Deerstalker Hat", 1)); TotalSalesReport report = gui.openSalesReport(); report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2))); report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1))); void sendAndProcess(OrderBuilder orderDetails) { Order order = orderDetails .withDefaultCustomersReference(nextCustomerReference()) .build(); requestSender.send(order); progressMonitor.waitForCompletion(order); } www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 13. What, Not How @Test public void reportsTotalSalesOfOrderedProducts() { havingReceived(anOrder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1)); havingReceived(anOrder().withLine("Deerstalker Hat", 1)); TotalSalesReport report = gui.openSalesReport(); report.displaysTotalSalesFor("Deerstalker Hat", equalTo(2)); report.displaysTotalSalesFor("Tweed Cape", equalTo(1)); } www.growing-object-oriented-software.com 2011 Test Diagnostics Mistakes are the portals of discovery. —James Joyce Friday, 9 September 2011
  • 14. Explain Yourself assertEquals(16301, customer.getBalance()); ComparisonFailure: expected:<[16301]> but was:<[16103]> assertEquals("balance", 16301, customer.getBalance()); ComparisonFailure: balance expected:<[16301]> but was:<[16103]> www.growing-object-oriented-software.com 2011 Describe Yourself ComparisonFailure: expected:<[a customer account id]> but was:<[id not set]> java.lang.AssertionError: payment date Expected: <Thu Jan 01 01:00:01 GMT 1970> got: <Thu Jan 01 01:00:02 GMT 1970> Date startDate = namedDate(1000, "startDate"); Date endDate = namedDate(2000, "endDate"); Date namedDate(long timeValue, final String name) { return new Date(timeValue) { public String toString() { return name; } }; } java.lang.AssertionError: payment date Expected: <startDate> got: <endDate> www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 15. Tracer Objects @RunWith(JMock.class) public class CustomerTest { final LineItem item1 = context.mock(LineItem.class, "item1"); final LineItem item2 = context.mock(LineItem.class, "item2"); final Billing billing = context.mock(Billing.class); @Test public void requestsInvoiceForPurchasedItems() { context.checking(new Expectations() {{ oneOf(billing).add(item1); oneOf(billing).add(item2); }}); customer.purchase(item1, item2); customer.requestInvoice(billing); } } not all expectations were satisfied expectations: expected once, already invoked 1 time: billing.add(<item1>) ! expected once, never invoked: billing.add(<item2>>) what happened before this: billing.add(<item1>) www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 16. Test Flexibility Living plants are flexible and tender; the dead are brittle and dry. [...] The rigid and stiff will be broken. The soft and yielding will overcome. —Lao Tzu (c.604—531 B.C.) Specify Precisely What Should Happen and No More www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 17. Interlude Information, Not Representation public interface CustomerBase { // Returns null if no customer found Customer findCustomerWithEmailAddress(String emailAddress); allowing(customerBase).findCustomerWithEmailAddress(theAddress); will(returnValue(null)); public static final Customer NO_CUSTOMER_FOUND = null; public interface CustomerBase { Maybe<Customer> findCustomerWithEmailAddress(String emailAddress); } www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 18. Precise Assertions assertThat(“strike price”, 92, equalTo(instrument.getStrikePrice())); assertThat(“transaction id”, instrument.getTransactionId(), largerThan(PREVIOUS_TRANSACTION_ID)); assertThat(failureMessage, allOf(containsString("strikePrice=92"), containsString("id=FGD.430"), containsString("is expired"))); www.growing-object-oriented-software.com 2011 Precise Expectations oneOf(auction).addAuctionEventListener(with(sniperForItem(itemId))); oneOf(auditTrail).recordFailure(with( allOf(containsString("strikePrice=92"), containsString("id=FGD.430"), containsString("is expired")))); www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 19. Allow Queries Expect Commands ignoring(auditTrail); allowing(catalog).getPriceForItem(item); will(returnValue(74)); exactly(2).of(order).addItem(item, 74); www.growing-object-oriented-software.com 2011 Only Enforce Order When It Matters @Test public void announcesMatchForOneAuction() { final AuctionSearcher auctionSearch = new AuctionSearcher(searchListener, asList(STUB_AUCTION1)); context.checking(new Expectations() {{ Sequence events = context.sequence("events"); oneOf(searchListener).searchMatched(STUB_AUCTION1); inSequence(events); oneOf(searchListener).searchFinished(); inSequence(events); }}); auctionSearch.searchFor(KEYWORDS); } www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 20. “Guinea Pig” Objects public class XmlMarshallerTest { public static class MarshalledObject { private String privateField = "private"; public transient String transientField = "transient"; public final String publicFinalField = "public final"; // constructors, accessors for private field, etc. } @Test public void marshallsAndUnmarshallsSerialisableFields() { XMLMarshaller marshaller = new XmlMarshaller(); MarshalledObject original = new MarshalledObject(); String xml = marshaller.marshall(original); MarshalledObject unmarshalled = marshaller.unmarshall(xml); assertThat(unmarshalled, hasSameSerialisableFieldsAs(original)); } } www.growing-object-oriented-software.com 2011 Tests Are Code Too • Expressiveness over convenience • Refactor and abstract • Focus on what matters • If it’s hard to test, that’s a clue www.growing-object-oriented-software.com 2011 Friday, 9 September 2011
  • 21. Other sources www.growing-object-oriented-software.com 2011 And, of course… Friday, 9 September 2011