The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Unit Testing Asynchronous Code

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Joe Walnes

Posts: 151
Nickname: jwalnes1
Registered: Aug, 2003

Joe Walnes, "The Developers' Coach" from ThoughtWorks
Unit Testing Asynchronous Code Posted: May 25, 2004 10:43 AM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by Joe Walnes.
Original Post: Unit Testing Asynchronous Code
Feed Title: Joe's New Jelly
Feed URL: http://joe.truemesh.com/blog/index.rdf
Feed Description: The musings of a ThoughtWorker obsessed with Agile, XP, maintainability, Java, .NET, Ruby and OpenSource. Mmm'kay?
Latest Agile Buzz Posts
Latest Agile Buzz Posts by Joe Walnes
Latest Posts From Joe's New Jelly

Advertisement

I try to avoid using code that instantiates threads from unit-tests. They're awkward to write, brittle and I would rather extract the controlling thread, using the test as the controller. However there are times when it's unavoidable.

Here's an example. A PriceFinder is a class that goes and retrieves a price for a symbol asyncronously, returning immediately. Sometime later it receives a response and performs a callback on a handler. I've left out the implementation of how it actually does this (maybe a web-service, database call or JMS message).

public class PriceFinder {
  public void findPrice(String symbol, PriceHandler handler) { ... }
}

public interface PriceHandler {
  void foundPrice(Price price);
}

To test this, a handler can be used from the test case that records what is passed in. The test can then wait for a specified time and assert that the correct result is received. If the asyncronous code does not complete within the specified time, the test will fail (suggesting the code is either running very slowly or is never going to complete).

public class PriceFinderTest extends TestCase {

  private PriceFinder finder = new PriceFinder();
  private Price receivedPrice;

  public void testRetrievesAValidPrice() throws Exception {
    finder.findPrice("MSFT", new PriceHandler() {
      public void foundPrice(Price price) {
        receivedPrice = price;
      }
    });

    // Smelly!
    Thread.sleep(2000); // Wait two seconds.
    assertNotNull("Expected a price", receivedPrice);
  }

}

However, this sucks as it will slow your test suite right down if you have loads of tests using Thread.sleep().

A less time consuming way to do it is by using wait() and notify() on a lock. The handler notifies the lock and the test waits for this notification. In case this notification never happens, a timeout is used when calling wait().

public class PriceFinderTest extends TestCase {

  private PriceFinder finder = new PriceFinder();
  private Price receivedPrice;
  private Object lock = new Object();

  public void testRetrievesAValidPrice() throws Exception {
    finder.findPrice("MSFT", new PriceHandler() {
      public void foundPrice(Price price) {
        receivedPrice = price;
        synchronized(lock) {
          lock.notify();
        }
      }
    });

    synchronized(lock) {
      lock.wait(2000); // Wait two seconds or until the 
                       // monitor has been notified.
                       // But there's still a problem...
    } 
    assertNotNull("Expected a price", receivedPrice);
  }

}

This optimistic approach results in fast running tests while all is good. If the PriceFinder is behaving well, the test will not wait any longer than the PriceFinder takes to complete its work. If PriceFinder has a bug in it and never calls the handler, the test will fail in at most two seconds.

However, there's still a subtle issue. In the case that the PriceFinder is really fast, it may call notify() before the test starts wait()ing. The test will still pass, but it will wait until the timeout occurs.

This is where threading and synchronization get messy and beyond me. Doug Lea has a nice little class called Latch in his concurrency library (available in JDK 1.5 as CountDownLatch). A latch can only be locked once, once released it will never lock again.

public class PriceFinderTest extends TestCase {

  private PriceFinder finder = new PriceFinder();
  private Price receivedPrice;
  private Latch latch = new Latch();

  public void testRetrievesAValidPrice() throws Exception {
    finder.findPrice("MSFT", new PriceHandler() {
      public void foundPrice(Price price) {
        receivedPrice = price;
        latch.release();
      }
    });

    latch.attempt(2000); // Wait until the latch is released
                          // or a timeout occurs.
    assertNotNull("Expected a price", receivedPrice);
  }

}

That'll do it.

Read: Unit Testing Asynchronous Code

Topic: Whoa - secret marketing Previous Topic   Next Topic Topic: Interesting LtU thread on messages and messaging

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use